9
0
mirror of https://github.com/WiIIiam278/HuskSync.git synced 2025-12-22 16:19:20 +00:00

Compare commits

..

25 Commits
3.2.1 ... 3.3

Author SHA1 Message Date
William
fa7f6f0d6e fix: exception when reading server defaults 2024-01-26 14:55:09 +00:00
William
267cf1ff35 fix: wrong URL on startup exception 2024-01-26 13:57:52 +00:00
William
08944ffd35 refactor: update a few config comments 2024-01-26 13:48:46 +00:00
William
c75114b858 deps: bump ConfigLib to 4.3.0 2024-01-26 13:40:46 +00:00
William
350a8b864d fix: bad ConfigProvider logic 2024-01-26 00:00:18 +00:00
William278
df0bd7a7cb refactor: actually don't use lombok for API just yet 2024-01-25 15:46:07 +00:00
William278
9fc9e8caf4 refactor: use lombok in a few other places 2024-01-25 15:44:36 +00:00
William278
2e3db2fffa refactor: use Guava methods in various places 2024-01-25 15:42:30 +00:00
William
530b3ef24d refactor: Migrate from BoostedYaml to Exll's ConfigLib (#233)
* feat: start work on moving to Exll's configlib

* refactor: Fully migrate to Exlll's configlib

* refactor: Optimize imports
2024-01-25 15:37:04 +00:00
William278
a9bd4dd2f0 build: stop trying to be clever with gradle publishing
if 'i aint readin allat' was a build scripting language
2024-01-24 23:34:47 +00:00
William278
85706d97c5 refactor: move unregister to common API module 2024-01-24 23:32:35 +00:00
William278
f7e3104e6b build: remove unnecessary "plugin" module 2024-01-24 23:30:39 +00:00
William278
f56d7f6113 docs: Fix API platforms section typo 2024-01-24 23:26:14 +00:00
William278
685431a40d api: add cross-platform API support 2024-01-24 23:25:37 +00:00
William278
9da3ff5281 build: Start minimizing built jars 2024-01-24 23:11:59 +00:00
William278
24453d0e1a build: Require Java 17, Minecraft 1.17.1 2024-01-24 23:06:25 +00:00
William278
280e90e297 refactor: use guard clause in thread unlock logic 2024-01-24 23:00:41 +00:00
Rubén
31920d056d refactor: Reconnect to Redis when connection lost (#230)
* Add redis reconnection

* Add separated method to handle thread unlock

* Add reconnection time constant
2024-01-22 12:53:56 +00:00
William278
6641e11fd9 fix: high latency redis environments firing data updates twice 2024-01-20 17:30:22 +00:00
William278
66bbde0b5d command: update translator credits in AboutMenu 2024-01-19 16:33:56 +00:00
WinTone01
7dde6423e4 Update tr-tr.yml (#228) 2024-01-19 16:32:16 +00:00
William278
0eac12e3f8 locales: Add id-id, courtesy of Wirayuda5620 2024-01-18 19:17:56 +00:00
Wirayuda5620
5df58e4ef9 Update HuskSyncCommand.java AboutMenu
hehe 😋
2024-01-19 00:36:59 +07:00
Wirayuda5620
4a6583d8bd Indonesian translation for HuskSync 2024-01-19 00:34:52 +07:00
jhqwqmc
059ee6f660 locales: Update zh-cn.yml (#224)
Correction
2024-01-13 13:06:58 +00:00
67 changed files with 1903 additions and 1832 deletions

View File

@@ -1,19 +0,0 @@
#!/bin/bash
JV=$(java -version 2>&1 >/dev/null | head -1)
echo "$JV" | sed -E 's/^.*version "([^".]*)\.[^"]*".*$/\1/'
if [ "$JV" != 16 ]; then
case "$1" in
install)
echo "installing sdkman..."
curl -s "https://get.sdkman.io" | bash
source ~/.sdkman/bin/sdkman-init.sh
sdk install java 16.0.1-open
;;
use)
echo "must source ~/.sdkman/bin/sdkman-init.sh"
exit 1
;;
esac
fi

View File

@@ -44,7 +44,7 @@
**Ready?** [It's syncing time!](https://william278.net/docs/husksync/setup) **Ready?** [It's syncing time!](https://william278.net/docs/husksync/setup)
## Setup ## Setup
Requires a MySQL (v8.0+) database, a Redis (v5.0+) server and any number of Spigot-based 1.16.5+ Minecraft servers, running Java 16+. Requires a MySQL (v8.0+) database, a Redis (v5.0+) server and any number of Spigot-based 1.17.1+ Minecraft servers, running Java 17+.
1. Place the plugin jar file in the /plugins/ directory of each Spigot server. You do not need to install HuskSync as a proxy plugin. 1. Place the plugin jar file in the /plugins/ directory of each Spigot server. You do not need to install HuskSync as a proxy plugin.
2. Start, then stop every server to let HuskSync generate the config file. 2. Start, then stop every server to let HuskSync generate the config file.

View File

@@ -1,3 +1,5 @@
import org.apache.tools.ant.filters.ReplaceTokens
plugins { plugins {
id 'com.github.johnrengelman.shadow' version '8.1.1' id 'com.github.johnrengelman.shadow' version '8.1.1'
id 'org.cadixdev.licenser' version '0.6.1' apply false id 'org.cadixdev.licenser' version '0.6.1' apply false
@@ -21,7 +23,36 @@ ext {
set 'snappy_version', snappy_version.toString() set 'snappy_version', snappy_version.toString()
} }
import org.apache.tools.ant.filters.ReplaceTokens publishing {
repositories {
if (System.getenv("RELEASES_MAVEN_USERNAME") != null) {
maven {
name = "william278-releases"
url = "https://repo.william278.net/releases"
credentials {
username = System.getenv("RELEASES_MAVEN_USERNAME")
password = System.getenv("RELEASES_MAVEN_PASSWORD")
}
authentication {
basic(BasicAuthentication)
}
}
}
if (System.getenv("SNAPSHOTS_MAVEN_USERNAME") != null) {
maven {
name = "william278-snapshots"
url = "https://repo.william278.net/snapshots"
credentials {
username = System.getenv("SNAPSHOTS_MAVEN_USERNAME")
password = System.getenv("SNAPSHOTS_MAVEN_PASSWORD")
}
authentication {
basic(BasicAuthentication)
}
}
}
}
}
allprojects { allprojects {
apply plugin: 'com.github.johnrengelman.shadow' apply plugin: 'com.github.johnrengelman.shadow'
@@ -29,11 +60,10 @@ allprojects {
apply plugin: 'java' apply plugin: 'java'
compileJava.options.encoding = 'UTF-8' compileJava.options.encoding = 'UTF-8'
compileJava.options.release.set 17
javadoc.options.encoding = 'UTF-8' javadoc.options.encoding = 'UTF-8'
javadoc.options.addStringOption('Xdoclint:none', '-quiet') javadoc.options.addStringOption('Xdoclint:none', '-quiet')
compileJava.options.release.set 16
repositories { repositories {
mavenLocal() mavenLocal()
mavenCentral() mavenCentral()
@@ -64,9 +94,11 @@ allprojects {
} }
processResources { processResources {
filesMatching(['**/*.json', '**/*.yml']) {
filter ReplaceTokens as Class, beginToken: '${', endToken: '}', filter ReplaceTokens as Class, beginToken: '${', endToken: '}',
tokens: rootProject.ext.properties tokens: rootProject.ext.properties
} }
}
} }
subprojects { subprojects {
@@ -77,18 +109,13 @@ subprojects {
from '../LICENSE' from '../LICENSE'
} }
if (['paper'].contains(project.name)) {
compileJava.options.release.set 17
}
if (['bukkit', 'paper', 'plugin'].contains(project.name)) {
shadowJar { shadowJar {
destinationDirectory.set(file("$rootDir/target")) destinationDirectory.set(file("$rootDir/target"))
archiveClassifier.set('') archiveClassifier.set('')
} }
// API publishing // API publishing
if ('bukkit'.contains(project.name)) { if (['common', 'bukkit'].contains(project.name)) {
java { java {
withSourcesJar() withSourcesJar()
withJavadocJar() withJavadocJar()
@@ -102,43 +129,29 @@ subprojects {
shadowJar.dependsOn(sourcesJar, javadocJar) shadowJar.dependsOn(sourcesJar, javadocJar)
publishing { publishing {
repositories { if (['common'].contains(project.name)) {
if (System.getenv("RELEASES_MAVEN_USERNAME") != null) { publications {
maven { mavenJavaCommon(MavenPublication) {
name = "william278-releases" groupId = 'net.william278.husksync'
url = "https://repo.william278.net/releases" artifactId = 'husksync-common'
credentials { version = "$rootProject.version"
username = System.getenv("RELEASES_MAVEN_USERNAME") artifact shadowJar
password = System.getenv("RELEASES_MAVEN_PASSWORD") artifact sourcesJar
} artifact javadocJar
authentication {
basic(BasicAuthentication)
}
}
}
if (System.getenv("SNAPSHOTS_MAVEN_USERNAME") != null) {
maven {
name = "william278-snapshots"
url = "https://repo.william278.net/snapshots"
credentials {
username = System.getenv("SNAPSHOTS_MAVEN_USERNAME")
password = System.getenv("SNAPSHOTS_MAVEN_PASSWORD")
}
authentication {
basic(BasicAuthentication)
}
} }
} }
} }
if (['bukkit'].contains(project.name)) {
publications { publications {
mavenJava(MavenPublication) { mavenJavaBukkit(MavenPublication) {
groupId = 'net.william278' groupId = 'net.william278.husksync'
artifactId = 'husksync' artifactId = 'husksync-bukkit'
version = "$rootProject.version" version = "$rootProject.version"
artifact shadowJar artifact shadowJar
artifact javadocJar
artifact sourcesJar artifact sourcesJar
artifact javadocJar
}
} }
} }
} }
@@ -146,7 +159,6 @@ subprojects {
jar.dependsOn(shadowJar) jar.dependsOn(shadowJar)
clean.delete "$rootDir/target" clean.delete "$rootDir/target"
}
} }
logger.lifecycle("Building HuskSync ${version} by William278") logger.lifecycle("Building HuskSync ${version} by William278")

View File

@@ -12,48 +12,48 @@ dependencies {
implementation 'space.arim.morepaperlib:morepaperlib:0.4.3' implementation 'space.arim.morepaperlib:morepaperlib:0.4.3'
implementation 'de.tr7zw:item-nbt-api:2.12.2' implementation 'de.tr7zw:item-nbt-api:2.12.2'
compileOnly 'org.spigotmc:spigot-api:1.16.5-R0.1-SNAPSHOT' compileOnly 'org.spigotmc:spigot-api:1.17.1-R0.1-SNAPSHOT'
compileOnly 'org.projectlombok:lombok:1.18.30'
compileOnly 'commons-io:commons-io:2.15.1' compileOnly 'commons-io:commons-io:2.15.1'
compileOnly 'org.json:json:20231013' compileOnly 'org.json:json:20231013'
compileOnly 'de.themoep:minedown-adventure:1.7.2-SNAPSHOT' compileOnly 'de.themoep:minedown-adventure:1.7.2-SNAPSHOT'
compileOnly 'dev.dejvokep:boosted-yaml:1.3.1' compileOnly 'com.github.Exlll.ConfigLib:configlib-yaml:v4.4.0'
compileOnly 'com.zaxxer:HikariCP:5.1.0' compileOnly 'com.zaxxer:HikariCP:5.1.0'
compileOnly 'net.william278:DesertWell:2.0.4' compileOnly 'net.william278:DesertWell:2.0.4'
compileOnly 'net.william278:annotaml:2.0.7'
compileOnly 'net.william278:AdvancementAPI:97a9583413' compileOnly 'net.william278:AdvancementAPI:97a9583413'
compileOnly "redis.clients:jedis:$jedis_version" compileOnly "redis.clients:jedis:$jedis_version"
annotationProcessor 'org.projectlombok:lombok:1.18.30'
} }
shadowJar { shadowJar {
dependencies { dependencies {
exclude(dependency('com.mojang:brigadier')) exclude(dependency('com.mojang:brigadier'))
} }
relocate 'org.apache.commons.io', 'net.william278.husksync.libraries.commons.io' relocate 'org.apache.commons.io', 'net.william278.husksync.libraries.commons.io'
relocate 'org.apache.commons.text', 'net.william278.husksync.libraries.commons.text' relocate 'org.apache.commons.text', 'net.william278.husksync.libraries.commons.text'
relocate 'org.apache.commons.lang3', 'net.william278.husksync.libraries.commons.lang3' relocate 'org.apache.commons.lang3', 'net.william278.husksync.libraries.commons.lang3'
relocate 'com.google.gson', 'net.william278.husksync.libraries.gson' relocate 'com.google.gson', 'net.william278.husksync.libraries.gson'
relocate 'org.json', 'net.william278.husksync.libraries.json'
relocate 'com.fatboyindustrial', 'net.william278.husksync.libraries' relocate 'com.fatboyindustrial', 'net.william278.husksync.libraries'
relocate 'de.themoep', 'net.william278.husksync.libraries' relocate 'de.themoep', 'net.william278.husksync.libraries'
relocate 'org.jetbrains', 'net.william278.husksync.libraries' relocate 'org.jetbrains', 'net.william278.husksync.libraries'
relocate 'org.intellij', 'net.william278.husksync.libraries' relocate 'org.intellij', 'net.william278.husksync.libraries'
relocate 'com.zaxxer', 'net.william278.husksync.libraries' relocate 'com.zaxxer', 'net.william278.husksync.libraries'
relocate 'dev.dejvokep', 'net.william278.husksync.libraries' relocate 'de.exlll', 'net.william278.huskclaims.libraries'
relocate 'net.william278.desertwell', 'net.william278.husksync.libraries.desertwell' relocate 'net.william278.desertwell', 'net.william278.husksync.libraries.desertwell'
relocate 'net.william278.paginedown', 'net.william278.husksync.libraries.paginedown' relocate 'net.william278.paginedown', 'net.william278.husksync.libraries.paginedown'
relocate 'net.william278.mapdataapi', 'net.william278.husksync.libraries.mapdataapi' relocate 'net.william278.mapdataapi', 'net.william278.husksync.libraries.mapdataapi'
relocate 'net.william278.andjam', 'net.william278.husksync.libraries.andjam' relocate 'net.william278.andjam', 'net.william278.husksync.libraries.andjam'
relocate 'net.querz', 'net.william278.husksync.libraries.nbtparser'
relocate 'net.roxeez', 'net.william278.husksync.libraries'
relocate 'me.lucko.commodore', 'net.william278.husksync.libraries.commodore'
relocate 'net.byteflux.libby', 'net.william278.husksync.libraries.libby'
relocate 'org.bstats', 'net.william278.husksync.libraries.bstats'
relocate 'dev.triumphteam.gui', 'net.william278.husksync.libraries.triumphgui'
relocate 'net.william278.mpdbconverter', 'net.william278.husksync.libraries.mpdbconverter' relocate 'net.william278.mpdbconverter', 'net.william278.husksync.libraries.mpdbconverter'
relocate 'net.william278.hslmigrator', 'net.william278.husksync.libraries.hslconverter' relocate 'net.william278.hslmigrator', 'net.william278.husksync.libraries.hslconverter'
relocate 'net.william278.annotaml', 'net.william278.husksync.libraries.annotaml' relocate 'org.json', 'net.william278.husksync.libraries.json'
relocate 'net.querz', 'net.william278.husksync.libraries.nbtparser'
relocate 'net.roxeez', 'net.william278.husksync.libraries'
relocate 'me.lucko.commodore', 'net.william278.husksync.libraries.commodore'
relocate 'org.bstats', 'net.william278.husksync.libraries.bstats'
relocate 'dev.triumphteam.gui', 'net.william278.husksync.libraries.triumphgui'
relocate 'space.arim.morepaperlib', 'net.william278.husksync.libraries.paperlib' relocate 'space.arim.morepaperlib', 'net.william278.husksync.libraries.paperlib'
relocate 'de.tr7zw.changeme.nbtapi', 'net.william278.husksync.libraries.nbtapi' relocate 'de.tr7zw.changeme.nbtapi', 'net.william278.husksync.libraries.nbtapi'
minimize()
} }

View File

@@ -19,7 +19,14 @@
package net.william278.husksync; package net.william278.husksync;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.gson.Gson; import com.google.gson.Gson;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import net.kyori.adventure.platform.AudienceProvider; import net.kyori.adventure.platform.AudienceProvider;
import net.kyori.adventure.platform.bukkit.BukkitAudiences; import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.william278.desertwell.util.Version; import net.william278.desertwell.util.Version;
@@ -64,14 +71,15 @@ import space.arim.morepaperlib.scheduling.AsynchronousScheduler;
import space.arim.morepaperlib.scheduling.GracefulScheduling; import space.arim.morepaperlib.scheduling.GracefulScheduling;
import space.arim.morepaperlib.scheduling.RegionalScheduler; import space.arim.morepaperlib.scheduling.RegionalScheduler;
import java.nio.file.Path;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class BukkitHuskSync extends JavaPlugin implements HuskSync, BukkitTask.Supplier, BukkitEventDispatcher, @Getter
BukkitMapPersister { @NoArgsConstructor
public class BukkitHuskSync extends JavaPlugin implements HuskSync, BukkitTask.Supplier,
BukkitEventDispatcher, BukkitMapPersister {
/** /**
* Metrics ID for <a href="https://bstats.org/plugin/bukkit/HuskSync%20-%20Bukkit/13140">HuskSync on Bukkit</a>. * Metrics ID for <a href="https://bstats.org/plugin/bukkit/HuskSync%20-%20Bukkit/13140">HuskSync on Bukkit</a>.
@@ -79,26 +87,31 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync, BukkitTask.S
private static final int METRICS_ID = 13140; private static final int METRICS_ID = 13140;
private static final String PLATFORM_TYPE_ID = "bukkit"; private static final String PLATFORM_TYPE_ID = "bukkit";
private final Map<Identifier, Serializer<? extends Data>> serializers = Maps.newLinkedHashMap();
private final Map<UUID, Map<Identifier, Data>> playerCustomDataStore = Maps.newConcurrentMap();
private final Map<Integer, MapView> mapViews = Maps.newConcurrentMap();
private final List<Migrator> availableMigrators = Lists.newArrayList();
private final Set<UUID> lockedPlayers = Sets.newConcurrentHashSet();
private boolean disabling;
private Gson gson;
private AudienceProvider audiences;
private MorePaperLib paperLib;
private Database database; private Database database;
private RedisManager redisManager; private RedisManager redisManager;
private EventListener eventListener; private EventListener eventListener;
private DataAdapter dataAdapter; private DataAdapter dataAdapter;
private Map<Identifier, Serializer<? extends Data>> serializers;
private Map<UUID, Map<Identifier, Data>> playerCustomDataStore;
private Set<UUID> lockedPlayers;
private DataSyncer dataSyncer; private DataSyncer dataSyncer;
private Settings settings;
private Locales locales;
private Server server;
private List<Migrator> availableMigrators;
private LegacyConverter legacyConverter; private LegacyConverter legacyConverter;
private Map<Integer, MapView> mapViews;
private BukkitAudiences audiences;
private MorePaperLib paperLib;
private AsynchronousScheduler asyncScheduler; private AsynchronousScheduler asyncScheduler;
private RegionalScheduler regionalScheduler; private RegionalScheduler regionalScheduler;
private Gson gson; @Setter
private boolean disabling; private Settings settings;
@Setter
private Locales locales;
@Setter
@Getter(AccessLevel.NONE)
private Server serverName;
@Override @Override
public void onEnable() { public void onEnable() {
@@ -107,18 +120,17 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync, BukkitTask.S
this.gson = createGson(); this.gson = createGson();
this.audiences = BukkitAudiences.create(this); this.audiences = BukkitAudiences.create(this);
this.paperLib = new MorePaperLib(this); this.paperLib = new MorePaperLib(this);
this.availableMigrators = new ArrayList<>();
this.serializers = new LinkedHashMap<>();
this.lockedPlayers = new ConcurrentSkipListSet<>();
this.playerCustomDataStore = new ConcurrentHashMap<>();
this.mapViews = new ConcurrentHashMap<>();
// Load settings and locales // Load settings and locales
initialize("plugin config & locale files", (plugin) -> this.loadConfigs()); initialize("plugin config & locale files", (plugin) -> {
loadSettings();
loadLocales();
loadServer();
});
// Prepare data adapter // Prepare data adapter
initialize("data adapter", (plugin) -> { initialize("data adapter", (plugin) -> {
if (settings.doCompressData()) { if (settings.getSynchronization().isCompressData()) {
dataAdapter = new SnappyGsonAdapter(this); dataAdapter = new SnappyGsonAdapter(this);
} else { } else {
dataAdapter = new GsonAdapter(this); dataAdapter = new GsonAdapter(this);
@@ -150,7 +162,7 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync, BukkitTask.S
}); });
// Initialize the database // Initialize the database
initialize(getSettings().getDatabaseType().getDisplayName() + " database connection", (plugin) -> { initialize(getSettings().getDatabase().getType().getDisplayName() + " database connection", (plugin) -> {
this.database = new MySqlDatabase(this); this.database = new MySqlDatabase(this);
this.database.initialize(); this.database.initialize();
}); });
@@ -163,7 +175,7 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync, BukkitTask.S
// Prepare data syncer // Prepare data syncer
initialize("data syncer", (plugin) -> { initialize("data syncer", (plugin) -> {
dataSyncer = getSettings().getSyncMode().create(this); dataSyncer = getSettings().getSynchronization().getMode().create(this);
dataSyncer.initialize(); dataSyncer.initialize();
}); });
@@ -175,7 +187,7 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync, BukkitTask.S
// Register plugin hooks // Register plugin hooks
initialize("hooks", (plugin) -> { initialize("hooks", (plugin) -> {
if (isDependencyLoaded("Plan") && getSettings().usePlanHook()) { if (isDependencyLoaded("Plan") && getSettings().isEnablePlanHook()) {
new PlanHook(this).hookIntoPlan(); new PlanHook(this).hookIntoPlan();
} }
}); });
@@ -232,91 +244,27 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync, BukkitTask.S
return Optional.of(BukkitUser.adapt(player, this)); return Optional.of(BukkitUser.adapt(player, this));
} }
@Override
@NotNull
public Database getDatabase() {
return database;
}
@Override
@NotNull
public RedisManager getRedisManager() {
return redisManager;
}
@NotNull
@Override
public DataAdapter getDataAdapter() {
return dataAdapter;
}
@NotNull
@Override
public DataSyncer getDataSyncer() {
return dataSyncer;
}
@Override @Override
public void setDataSyncer(@NotNull DataSyncer dataSyncer) { public void setDataSyncer(@NotNull DataSyncer dataSyncer) {
log(Level.INFO, String.format("Switching data syncer to %s", dataSyncer.getClass().getSimpleName())); log(Level.INFO, String.format("Switching data syncer to %s", dataSyncer.getClass().getSimpleName()));
this.dataSyncer = dataSyncer; this.dataSyncer = dataSyncer;
} }
@NotNull
@Override
@SuppressWarnings("unchecked")
public Map<Identifier, Serializer<? extends Data>> getSerializers() {
return serializers;
}
@NotNull
@Override
public List<Migrator> getAvailableMigrators() {
return availableMigrators;
}
@NotNull @NotNull
@Override @Override
public Map<Identifier, Data> getPlayerCustomDataStore(@NotNull OnlineUser user) { public Map<Identifier, Data> getPlayerCustomDataStore(@NotNull OnlineUser user) {
if (playerCustomDataStore.containsKey(user.getUuid())) { if (playerCustomDataStore.containsKey(user.getUuid())) {
return playerCustomDataStore.get(user.getUuid()); return playerCustomDataStore.get(user.getUuid());
} }
final Map<Identifier, Data> data = new HashMap<>(); final Map<Identifier, Data> data = Maps.newHashMap();
playerCustomDataStore.put(user.getUuid(), data); playerCustomDataStore.put(user.getUuid(), data);
return data; return data;
} }
@Override @Override
@NotNull @NotNull
public Settings getSettings() {
return settings;
}
@Override
public void setSettings(@NotNull Settings settings) {
this.settings = settings;
}
@NotNull
@Override
public String getServerName() { public String getServerName() {
return server.getName(); return serverName == null ? "server" : serverName.getName();
}
@Override
public void setServer(@NotNull Server server) {
this.server = server;
}
@Override
@NotNull
public Locales getLocales() {
return locales;
}
@Override
public void setLocales(@NotNull Locales locales) {
this.locales = locales;
} }
@Override @Override
@@ -369,28 +317,6 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync, BukkitTask.S
return Optional.of(legacyConverter); return Optional.of(legacyConverter);
} }
@NotNull
@Override
public Set<UUID> getLockedPlayers() {
return lockedPlayers;
}
@NotNull
@Override
public Gson getGson() {
return gson;
}
@Override
public boolean isDisabling() {
return disabling;
}
@NotNull
public Map<Integer, MapView> getMapViews() {
return mapViews;
}
@NotNull @NotNull
public GracefulScheduling getScheduler() { public GracefulScheduling getScheduler() {
return paperLib.scheduling(); return paperLib.scheduling();
@@ -408,16 +334,17 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync, BukkitTask.S
? regionalScheduler = getScheduler().globalRegionalScheduler() : regionalScheduler; ? regionalScheduler = getScheduler().globalRegionalScheduler() : regionalScheduler;
} }
@NotNull
public AudienceProvider getAudiences() {
return audiences;
}
@NotNull @NotNull
public CommandRegistration getCommandRegistrar() { public CommandRegistration getCommandRegistrar() {
return paperLib.commandRegistration(); return paperLib.commandRegistration();
} }
@Override
@NotNull
public Path getConfigDirectory() {
return getDataFolder().toPath();
}
@Override @Override
@NotNull @NotNull
public HuskSync getPlugin() { public HuskSync getPlugin() {

View File

@@ -43,9 +43,6 @@ import java.util.function.Consumer;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class BukkitHuskSyncAPI extends HuskSyncAPI { public class BukkitHuskSyncAPI extends HuskSyncAPI {
// Instance of the plugin
private static BukkitHuskSyncAPI instance;
/** /**
* <b>(Internal use only)</b> - Constructor, instantiating the API. * <b>(Internal use only)</b> - Constructor, instantiating the API.
*/ */
@@ -55,7 +52,7 @@ public class BukkitHuskSyncAPI extends HuskSyncAPI {
} }
/** /**
* Entrypoint to the HuskSync API - returns an instance of the API * Entrypoint to the HuskSync API on the bukkit platform - returns an instance of the API
* *
* @return instance of the HuskSync API * @return instance of the HuskSync API
* @since 3.0 * @since 3.0
@@ -65,7 +62,7 @@ public class BukkitHuskSyncAPI extends HuskSyncAPI {
if (instance == null) { if (instance == null) {
throw new NotRegisteredException(); throw new NotRegisteredException();
} }
return instance; return (BukkitHuskSyncAPI) instance;
} }
/** /**
@@ -79,14 +76,6 @@ public class BukkitHuskSyncAPI extends HuskSyncAPI {
instance = new BukkitHuskSyncAPI(plugin); instance = new BukkitHuskSyncAPI(plugin);
} }
/**
* <b>(Internal use only)</b> - Unregister the API for this platform.
*/
@ApiStatus.Internal
public static void unregister() {
instance = null;
}
/** /**
* Returns a {@link OnlineUser} instance for the given bukkit {@link Player}. * Returns a {@link OnlineUser} instance for the given bukkit {@link Player}.
* *

View File

@@ -98,7 +98,7 @@ public class BukkitCommand extends org.bukkit.command.Command {
} }
// Register commodore TAB completion // Register commodore TAB completion
if (CommodoreProvider.isSupported() && plugin.getSettings().doBrigadierTabCompletion()) { if (CommodoreProvider.isSupported() && plugin.getSettings().isBrigadierTabCompletion()) {
BrigadierUtil.registerCommodore(plugin, this, command); BrigadierUtil.registerCommodore(plugin, this, command);
} }
} }

View File

@@ -19,6 +19,8 @@
package net.william278.husksync.data; package net.william278.husksync.data;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import de.tr7zw.changeme.nbtapi.NBTCompound; import de.tr7zw.changeme.nbtapi.NBTCompound;
import de.tr7zw.changeme.nbtapi.NBTPersistentDataContainer; import de.tr7zw.changeme.nbtapi.NBTPersistentDataContainer;
@@ -313,10 +315,10 @@ public abstract class BukkitData implements Data {
// Iterate through the server advancement set and add all advancements to the list // Iterate through the server advancement set and add all advancements to the list
@NotNull @NotNull
public static BukkitData.Advancements adapt(@NotNull Player player) { public static BukkitData.Advancements adapt(@NotNull Player player) {
final List<Advancement> advancements = new ArrayList<>(); final List<Advancement> advancements = Lists.newArrayList();
forEachAdvancement(advancement -> { forEachAdvancement(advancement -> {
final AdvancementProgress advancementProgress = player.getAdvancementProgress(advancement); final AdvancementProgress advancementProgress = player.getAdvancementProgress(advancement);
final Map<String, Date> awardedCriteria = new HashMap<>(); final Map<String, Date> awardedCriteria = Maps.newHashMap();
advancementProgress.getAwardedCriteria().forEach(criteriaKey -> awardedCriteria.put(criteriaKey, advancementProgress.getAwardedCriteria().forEach(criteriaKey -> awardedCriteria.put(criteriaKey,
advancementProgress.getDateAwarded(criteriaKey))); advancementProgress.getDateAwarded(criteriaKey)));
@@ -822,7 +824,7 @@ public abstract class BukkitData implements Data {
// Set max health // Set max health
final AttributeInstance maxHealth = getMaxHealthAttribute(player); final AttributeInstance maxHealth = getMaxHealthAttribute(player);
try { try {
if (plugin.getSettings().doSynchronizeMaxHealth() && this.maxHealth != 0) { if (plugin.getSettings().getSynchronization().isSynchronizeMaxHealth() && this.maxHealth != 0) {
maxHealth.setBaseValue(this.maxHealth); maxHealth.setBaseValue(this.maxHealth);
} }
} catch (Throwable e) { } catch (Throwable e) {

View File

@@ -62,7 +62,8 @@ public interface BukkitUserDataHolder extends UserDataHolder {
@NotNull @NotNull
@Override @Override
default Optional<Data.Items.Inventory> getInventory() { default Optional<Data.Items.Inventory> getInventory() {
if ((isDead() && !getPlugin().getSettings().doSynchronizeDeadPlayersChangingServer())) { if ((isDead() && !getPlugin().getSettings().getSynchronization().getSaveOnDeath()
.isSyncDeadPlayersChangingServer())) {
return Optional.of(BukkitData.Items.Inventory.empty()); return Optional.of(BukkitData.Items.Inventory.empty());
} }
final PlayerInventory inventory = getBukkitPlayer().getInventory(); final PlayerInventory inventory = getBukkitPlayer().getInventory();

View File

@@ -57,13 +57,13 @@ public class BukkitEventListener extends EventListener implements BukkitJoinEven
public BukkitEventListener(@NotNull BukkitHuskSync huskSync) { public BukkitEventListener(@NotNull BukkitHuskSync huskSync) {
super(huskSync); super(huskSync);
this.blacklistedCommands = huskSync.getSettings().getBlacklistedCommandsWhileLocked(); this.blacklistedCommands = huskSync.getSettings().getSynchronization().getBlacklistedCommandsWhileLocked();
Bukkit.getServer().getPluginManager().registerEvents(this, huskSync); Bukkit.getServer().getPluginManager().registerEvents(this, huskSync);
} }
@Override @Override
public boolean handleEvent(@NotNull ListenerType type, @NotNull Priority priority) { public boolean handleEvent(@NotNull ListenerType type, @NotNull Priority priority) {
return plugin.getSettings().getEventPriority(type).equals(priority); return plugin.getSettings().getSynchronization().getEventPriority(type).equals(priority);
} }
@Override @Override
@@ -92,7 +92,7 @@ public class BukkitEventListener extends EventListener implements BukkitJoinEven
} }
// Handle saving player data snapshots on death // Handle saving player data snapshots on death
if (!plugin.getSettings().doSaveOnDeath()) { if (!plugin.getSettings().getSynchronization().getSaveOnDeath().isEnabled()) {
return; return;
} }
@@ -106,7 +106,7 @@ public class BukkitEventListener extends EventListener implements BukkitJoinEven
@EventHandler(ignoreCancelled = true) @EventHandler(ignoreCancelled = true)
public void onWorldSave(@NotNull WorldSaveEvent event) { public void onWorldSave(@NotNull WorldSaveEvent event) {
if (!plugin.getSettings().doSaveOnWorldSave()) { if (!plugin.getSettings().getSynchronization().isSaveOnWorldSave()) {
return; return;
} }
@@ -118,7 +118,7 @@ public class BukkitEventListener extends EventListener implements BukkitJoinEven
@EventHandler(ignoreCancelled = true) @EventHandler(ignoreCancelled = true)
public void onMapInitialize(@NotNull MapInitializeEvent event) { public void onMapInitialize(@NotNull MapInitializeEvent event) {
if (plugin.getSettings().doPersistLockedMaps() && event.getMap().isLocked()) { if (plugin.getSettings().getSynchronization().isPersistLockedMaps() && event.getMap().isLocked()) {
getPlugin().runAsync(() -> ((BukkitHuskSync) plugin).renderMapFromFile(event.getMap())); getPlugin().runAsync(() -> ((BukkitHuskSync) plugin).renderMapFromFile(event.getMap()));
} }
} }

View File

@@ -19,6 +19,8 @@
package net.william278.husksync.migrator; package net.william278.husksync.migrator;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.zaxxer.hikari.HikariDataSource; import com.zaxxer.hikari.HikariDataSource;
import me.william278.husksync.bukkit.data.DataSerializer; import me.william278.husksync.bukkit.data.DataSerializer;
import net.william278.hslmigrator.HSLConverter; import net.william278.hslmigrator.HSLConverter;
@@ -42,6 +44,8 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import static net.william278.husksync.config.Settings.DatabaseSettings;
public class LegacyMigrator extends Migrator { public class LegacyMigrator extends Migrator {
private final HSLConverter hslConverter; private final HSLConverter hslConverter;
@@ -56,11 +60,13 @@ public class LegacyMigrator extends Migrator {
public LegacyMigrator(@NotNull HuskSync plugin) { public LegacyMigrator(@NotNull HuskSync plugin) {
super(plugin); super(plugin);
this.hslConverter = HSLConverter.getInstance(); this.hslConverter = HSLConverter.getInstance();
this.sourceHost = plugin.getSettings().getMySqlHost();
this.sourcePort = plugin.getSettings().getMySqlPort(); final DatabaseSettings.DatabaseCredentials credentials = plugin.getSettings().getDatabase().getCredentials();
this.sourceUsername = plugin.getSettings().getMySqlUsername(); this.sourceHost = credentials.getHost();
this.sourcePassword = plugin.getSettings().getMySqlPassword(); this.sourcePort = credentials.getPort();
this.sourceDatabase = plugin.getSettings().getMySqlDatabase(); this.sourceUsername = credentials.getUsername();
this.sourcePassword = credentials.getPassword();
this.sourceDatabase = credentials.getDatabase();
this.sourcePlayersTable = "husksync_players"; this.sourcePlayersTable = "husksync_players";
this.sourceDataTable = "husksync_data"; this.sourceDataTable = "husksync_data";
} }
@@ -87,7 +93,7 @@ public class LegacyMigrator extends Migrator {
connectionPool.setPoolName((getIdentifier() + "_migrator_pool").toUpperCase(Locale.ENGLISH)); connectionPool.setPoolName((getIdentifier() + "_migrator_pool").toUpperCase(Locale.ENGLISH));
plugin.log(Level.INFO, "Downloading raw data from the legacy database (this might take a while)..."); plugin.log(Level.INFO, "Downloading raw data from the legacy database (this might take a while)...");
final List<LegacyData> dataToMigrate = new ArrayList<>(); final List<LegacyData> dataToMigrate = Lists.newArrayList();
try (final Connection connection = connectionPool.getConnection()) { try (final Connection connection = connectionPool.getConnection()) {
try (final PreparedStatement statement = connection.prepareStatement(""" try (final PreparedStatement statement = connection.prepareStatement("""
SELECT `uuid`, `username`, `inventory`, `ender_chest`, `health`, `max_health`, `health_scale`, `hunger`, `saturation`, `saturation_exhaustion`, `selected_slot`, `status_effects`, `total_experience`, `exp_level`, `exp_progress`, `game_mode`, `statistics`, `is_flying`, `advancements`, `location` SELECT `uuid`, `username`, `inventory`, `ender_chest`, `health`, `max_health`, `health_scale`, `hunger`, `saturation`, `saturation_exhaustion`, `selected_slot`, `status_effects`, `total_experience`, `exp_level`, `exp_progress`, `game_mode`, `statistics`, `is_flying`, `advancements`, `location`
@@ -338,7 +344,7 @@ public class LegacyMigrator extends Migrator {
} }
private Map<String, Integer> convertStatisticMap(@NotNull HashMap<Statistic, Integer> rawMap) { private Map<String, Integer> convertStatisticMap(@NotNull HashMap<Statistic, Integer> rawMap) {
final HashMap<String, Integer> convertedMap = new HashMap<>(); final HashMap<String, Integer> convertedMap = Maps.newHashMap();
for (Map.Entry<Statistic, Integer> entry : rawMap.entrySet()) { for (Map.Entry<Statistic, Integer> entry : rawMap.entrySet()) {
convertedMap.put(entry.getKey().getKey().toString(), entry.getValue()); convertedMap.put(entry.getKey().getKey().toString(), entry.getValue());
} }
@@ -346,7 +352,7 @@ public class LegacyMigrator extends Migrator {
} }
private Map<String, Map<String, Integer>> convertMaterialStatisticMap(@NotNull HashMap<Statistic, HashMap<Material, Integer>> rawMap) { private Map<String, Map<String, Integer>> convertMaterialStatisticMap(@NotNull HashMap<Statistic, HashMap<Material, Integer>> rawMap) {
final Map<String, Map<String, Integer>> convertedMap = new HashMap<>(); final Map<String, Map<String, Integer>> convertedMap = Maps.newHashMap();
for (Map.Entry<Statistic, HashMap<Material, Integer>> entry : rawMap.entrySet()) { for (Map.Entry<Statistic, HashMap<Material, Integer>> entry : rawMap.entrySet()) {
for (Map.Entry<Material, Integer> materialEntry : entry.getValue().entrySet()) { for (Map.Entry<Material, Integer> materialEntry : entry.getValue().entrySet()) {
convertedMap.computeIfAbsent(entry.getKey().getKey().toString(), k -> new HashMap<>()) convertedMap.computeIfAbsent(entry.getKey().getKey().toString(), k -> new HashMap<>())
@@ -357,7 +363,7 @@ public class LegacyMigrator extends Migrator {
} }
private Map<String, Map<String, Integer>> convertEntityStatisticMap(@NotNull HashMap<Statistic, HashMap<EntityType, Integer>> rawMap) { private Map<String, Map<String, Integer>> convertEntityStatisticMap(@NotNull HashMap<Statistic, HashMap<EntityType, Integer>> rawMap) {
final Map<String, Map<String, Integer>> convertedMap = new HashMap<>(); final Map<String, Map<String, Integer>> convertedMap = Maps.newHashMap();
for (Map.Entry<Statistic, HashMap<EntityType, Integer>> entry : rawMap.entrySet()) { for (Map.Entry<Statistic, HashMap<EntityType, Integer>> entry : rawMap.entrySet()) {
for (Map.Entry<EntityType, Integer> materialEntry : entry.getValue().entrySet()) { for (Map.Entry<EntityType, Integer> materialEntry : entry.getValue().entrySet()) {
convertedMap.computeIfAbsent(entry.getKey().getKey().toString(), k -> new HashMap<>()) convertedMap.computeIfAbsent(entry.getKey().getKey().toString(), k -> new HashMap<>())

View File

@@ -19,6 +19,7 @@
package net.william278.husksync.migrator; package net.william278.husksync.migrator;
import com.google.common.collect.Lists;
import com.zaxxer.hikari.HikariDataSource; import com.zaxxer.hikari.HikariDataSource;
import net.william278.husksync.BukkitHuskSync; import net.william278.husksync.BukkitHuskSync;
import net.william278.husksync.HuskSync; import net.william278.husksync.HuskSync;
@@ -35,12 +36,17 @@ import org.jetbrains.annotations.NotNull;
import java.sql.Connection; import java.sql.Connection;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.util.*; import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import static net.william278.husksync.config.Settings.DatabaseSettings;
/** /**
* A migrator for migrating MySQLPlayerDataBridge data to HuskSync {@link DataSnapshot}s * A migrator for migrating MySQLPlayerDataBridge data to HuskSync {@link DataSnapshot}s
*/ */
@@ -62,11 +68,12 @@ public class MpdbMigrator extends Migrator {
Bukkit.getPluginManager().getPlugin("MySQLPlayerDataBridge"), Bukkit.getPluginManager().getPlugin("MySQLPlayerDataBridge"),
"MySQLPlayerDataBridge dependency not found!" "MySQLPlayerDataBridge dependency not found!"
)); ));
this.sourceHost = plugin.getSettings().getMySqlHost(); final DatabaseSettings.DatabaseCredentials credentials = plugin.getSettings().getDatabase().getCredentials();
this.sourcePort = plugin.getSettings().getMySqlPort(); this.sourceHost = credentials.getHost();
this.sourceUsername = plugin.getSettings().getMySqlUsername(); this.sourcePort = credentials.getPort();
this.sourcePassword = plugin.getSettings().getMySqlPassword(); this.sourceUsername = credentials.getUsername();
this.sourceDatabase = plugin.getSettings().getMySqlDatabase(); this.sourcePassword = credentials.getPassword();
this.sourceDatabase = credentials.getDatabase();
this.sourceInventoryTable = "mpdb_inventory"; this.sourceInventoryTable = "mpdb_inventory";
this.sourceEnderChestTable = "mpdb_enderchest"; this.sourceEnderChestTable = "mpdb_enderchest";
this.sourceExperienceTable = "mpdb_experience"; this.sourceExperienceTable = "mpdb_experience";
@@ -95,7 +102,7 @@ public class MpdbMigrator extends Migrator {
connectionPool.setPoolName((getIdentifier() + "_migrator_pool").toUpperCase(Locale.ENGLISH)); connectionPool.setPoolName((getIdentifier() + "_migrator_pool").toUpperCase(Locale.ENGLISH));
plugin.log(Level.INFO, "Downloading raw data from the MySQLPlayerDataBridge database (this might take a while)..."); plugin.log(Level.INFO, "Downloading raw data from the MySQLPlayerDataBridge database (this might take a while)...");
final List<MpdbData> dataToMigrate = new ArrayList<>(); final List<MpdbData> dataToMigrate = Lists.newArrayList();
try (final Connection connection = connectionPool.getConnection()) { try (final Connection connection = connectionPool.getConnection()) {
try (final PreparedStatement statement = connection.prepareStatement(""" try (final PreparedStatement statement = connection.prepareStatement("""
SELECT `%source_inventory_table%`.`player_uuid`, `%source_inventory_table%`.`player_name`, `inventory`, `armor`, `enderchest`, `exp_lvl`, `exp`, `total_exp` SELECT `%source_inventory_table%`.`player_uuid`, `%source_inventory_table%`.`player_name`, `inventory`, `armor`, `enderchest`, `exp_lvl`, `exp`, `total_exp`

View File

@@ -19,6 +19,7 @@
package net.william278.husksync.util; package net.william278.husksync.util;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import net.william278.husksync.HuskSync; import net.william278.husksync.HuskSync;
import net.william278.husksync.adapter.DataAdapter; import net.william278.husksync.adapter.DataAdapter;
@@ -54,7 +55,7 @@ public class BukkitLegacyConverter extends LegacyConverter {
@NotNull @NotNull
@Override @Override
public DataSnapshot.Packed convert(@NotNull byte[] data, @NotNull UUID id, public DataSnapshot.Packed convert(byte[] data, @NotNull UUID id,
@NotNull OffsetDateTime timestamp) throws DataAdapter.AdaptionException { @NotNull OffsetDateTime timestamp) throws DataAdapter.AdaptionException {
final JSONObject object = new JSONObject(plugin.getDataAdapter().bytesToString(data)); final JSONObject object = new JSONObject(plugin.getDataAdapter().bytesToString(data));
final int version = object.getInt("format_version"); final int version = object.getInt("format_version");
@@ -82,7 +83,7 @@ public class BukkitLegacyConverter extends LegacyConverter {
} }
final JSONObject status = object.getJSONObject("status_data"); final JSONObject status = object.getJSONObject("status_data");
final HashMap<Identifier, Data> containers = new HashMap<>(); final HashMap<Identifier, Data> containers = Maps.newHashMap();
if (shouldImport(Identifier.HEALTH)) { if (shouldImport(Identifier.HEALTH)) {
containers.put(Identifier.HEALTH, BukkitData.Health.from( containers.put(Identifier.HEALTH, BukkitData.Health.from(
status.getDouble("health"), status.getDouble("health"),
@@ -166,7 +167,7 @@ public class BukkitLegacyConverter extends LegacyConverter {
} }
final JSONArray advancements = object.getJSONArray("advancements"); final JSONArray advancements = object.getJSONArray("advancements");
final List<Data.Advancements.Advancement> converted = new ArrayList<>(); final List<Data.Advancements.Advancement> converted = Lists.newArrayList();
advancements.iterator().forEachRemaining(o -> { advancements.iterator().forEachRemaining(o -> {
final JSONObject advancement = (JSONObject) JSONObject.wrap(o); final JSONObject advancement = (JSONObject) JSONObject.wrap(o);
final String key = advancement.getString("key"); final String key = advancement.getString("key");
@@ -213,7 +214,7 @@ public class BukkitLegacyConverter extends LegacyConverter {
final Map<Statistic, Map<EntityType, Integer>> entityStats = Maps.newHashMap(); final Map<Statistic, Map<EntityType, Integer>> entityStats = Maps.newHashMap();
entities.keys().forEachRemaining(stat -> { entities.keys().forEachRemaining(stat -> {
final JSONObject entityStat = entities.getJSONObject(stat); final JSONObject entityStat = entities.getJSONObject(stat);
final Map<EntityType, Integer> entityMap = new HashMap<>(); final Map<EntityType, Integer> entityMap = Maps.newHashMap();
entityStat.keys().forEachRemaining(entity -> entityMap.put(matchEntityType(entity), entityStat.getInt(entity))); entityStat.keys().forEachRemaining(entity -> entityMap.put(matchEntityType(entity), entityStat.getInt(entity)));
entityStats.put(matchStatistic(stat), entityMap); entityStats.put(matchStatistic(stat), entityMap);
}); });
@@ -269,6 +270,7 @@ public class BukkitLegacyConverter extends LegacyConverter {
} }
// Deserialize a single legacy item stack // Deserialize a single legacy item stack
@SuppressWarnings("unchecked")
@Nullable @Nullable
private static ItemStack deserializeLegacyItemStack(@Nullable Object serializedItemStack) { private static ItemStack deserializeLegacyItemStack(@Nullable Object serializedItemStack) {
return serializedItemStack != null ? ItemStack.deserialize((Map<String, Object>) serializedItemStack) : null; return serializedItemStack != null ? ItemStack.deserialize((Map<String, Object>) serializedItemStack) : null;
@@ -276,7 +278,7 @@ public class BukkitLegacyConverter extends LegacyConverter {
private boolean shouldImport(@NotNull Identifier type) { private boolean shouldImport(@NotNull Identifier type) {
return plugin.getSettings().isSyncFeatureEnabled(type); return plugin.getSettings().getSynchronization().isFeatureEnabled(type);
} }
@NotNull @NotNull

View File

@@ -19,6 +19,7 @@
package net.william278.husksync.util; package net.william278.husksync.util;
import com.google.common.collect.Lists;
import de.tr7zw.changeme.nbtapi.NBT; import de.tr7zw.changeme.nbtapi.NBT;
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT; import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT;
import de.tr7zw.changeme.nbtapi.iface.ReadableNBT; import de.tr7zw.changeme.nbtapi.iface.ReadableNBT;
@@ -62,7 +63,7 @@ public interface BukkitMapPersister {
*/ */
@NotNull @NotNull
default ItemStack[] persistLockedMaps(@NotNull ItemStack[] items, @NotNull Player delegateRenderer) { default ItemStack[] persistLockedMaps(@NotNull ItemStack[] items, @NotNull Player delegateRenderer) {
if (!getPlugin().getSettings().doPersistLockedMaps()) { if (!getPlugin().getSettings().getSynchronization().isPersistLockedMaps()) {
return items; return items;
} }
return forEachMap(items, map -> this.persistMapView(map, delegateRenderer)); return forEachMap(items, map -> this.persistMapView(map, delegateRenderer));
@@ -76,7 +77,7 @@ public interface BukkitMapPersister {
*/ */
@NotNull @NotNull
default ItemStack[] setMapViews(@NotNull ItemStack[] items) { default ItemStack[] setMapViews(@NotNull ItemStack[] items) {
if (!getPlugin().getSettings().doPersistLockedMaps()) { if (!getPlugin().getSettings().getSynchronization().isPersistLockedMaps()) {
return items; return items;
} }
return forEachMap(items, this::applyMapView); return forEachMap(items, this::applyMapView);
@@ -416,7 +417,7 @@ public interface BukkitMapPersister {
*/ */
@NotNull @NotNull
private MapData extractMapData() { private MapData extractMapData() {
final List<MapBanner> banners = new ArrayList<>(); final List<MapBanner> banners = Lists.newArrayList();
final String BANNER_PREFIX = "banner_"; final String BANNER_PREFIX = "banner_";
for (int i = 0; i < getCursors().size(); i++) { for (int i = 0; i < getCursors().size(); i++) {
final MapCursor cursor = getCursors().getCursor(i); final MapCursor cursor = getCursors().getCursor(i);

View File

@@ -1,7 +1,7 @@
name: 'HuskSync' name: 'HuskSync'
version: '${version}' version: '${version}'
main: 'net.william278.husksync.BukkitHuskSync' main: 'net.william278.husksync.BukkitHuskSync'
api-version: 1.16 api-version: 1.17
author: 'William278' author: 'William278'
description: '${description}' description: '${description}'
website: 'https://william278.net' website: 'https://william278.net'

View File

@@ -9,26 +9,30 @@ dependencies {
api 'org.json:json:20231013' api 'org.json:json:20231013'
api 'com.google.code.gson:gson:2.10.1' api 'com.google.code.gson:gson:2.10.1'
api 'com.fatboyindustrial.gson-javatime-serialisers:gson-javatime-serialisers:1.1.2' api 'com.fatboyindustrial.gson-javatime-serialisers:gson-javatime-serialisers:1.1.2'
api 'dev.dejvokep:boosted-yaml:1.3.1' api 'com.github.Exlll.ConfigLib:configlib-yaml:v4.4.0'
api 'net.william278:annotaml:2.0.7'
api 'net.william278:DesertWell:2.0.4' api 'net.william278:DesertWell:2.0.4'
api 'net.william278:PagineDown:1.1' api 'net.william278:PagineDown:1.1'
api('com.zaxxer:HikariCP:5.1.0') { api('com.zaxxer:HikariCP:5.1.0') {
exclude module: 'slf4j-api' exclude module: 'slf4j-api'
} }
compileOnly 'org.projectlombok:lombok:1.18.30'
compileOnly 'org.jetbrains:annotations:24.1.0'
compileOnly 'net.kyori:adventure-api:4.15.0' compileOnly 'net.kyori:adventure-api:4.15.0'
compileOnly 'net.kyori:adventure-platform-api:4.3.2' compileOnly 'net.kyori:adventure-platform-api:4.3.2'
compileOnly 'org.jetbrains:annotations:24.1.0' compileOnly 'com.google.guava:guava:33.0.0-jre'
compileOnly 'com.github.plan-player-analytics:Plan:5.5.2272' compileOnly 'com.github.plan-player-analytics:Plan:5.5.2272'
compileOnly "redis.clients:jedis:$jedis_version" compileOnly "redis.clients:jedis:$jedis_version"
compileOnly "com.mysql:mysql-connector-j:$mysql_driver_version" compileOnly "com.mysql:mysql-connector-j:$mysql_driver_version"
compileOnly "org.mariadb.jdbc:mariadb-java-client:$mariadb_driver_version" compileOnly "org.mariadb.jdbc:mariadb-java-client:$mariadb_driver_version"
compileOnly "org.xerial.snappy:snappy-java:$snappy_version" compileOnly "org.xerial.snappy:snappy-java:$snappy_version"
testImplementation 'com.github.plan-player-analytics:Plan:5.5.2272'
testImplementation "redis.clients:jedis:$jedis_version" testImplementation "redis.clients:jedis:$jedis_version"
testImplementation "org.xerial.snappy:snappy-java:$snappy_version" testImplementation "org.xerial.snappy:snappy-java:$snappy_version"
testCompileOnly 'dev.dejvokep:boosted-yaml:1.3.1' testImplementation 'com.google.guava:guava:33.0.0-jre'
testImplementation 'com.github.plan-player-analytics:Plan:5.5.2272'
testCompileOnly 'com.github.Exlll.ConfigLib:configlib-yaml:v4.4.0'
testCompileOnly 'org.jetbrains:annotations:24.1.0' testCompileOnly 'org.jetbrains:annotations:24.1.0'
annotationProcessor 'org.projectlombok:lombok:1.18.30'
} }

View File

@@ -24,11 +24,11 @@ import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import net.kyori.adventure.audience.Audience; import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.platform.AudienceProvider; import net.kyori.adventure.platform.AudienceProvider;
import net.william278.annotaml.Annotaml;
import net.william278.desertwell.util.ThrowingConsumer; import net.william278.desertwell.util.ThrowingConsumer;
import net.william278.desertwell.util.UpdateChecker; import net.william278.desertwell.util.UpdateChecker;
import net.william278.desertwell.util.Version; import net.william278.desertwell.util.Version;
import net.william278.husksync.adapter.DataAdapter; import net.william278.husksync.adapter.DataAdapter;
import net.william278.husksync.config.ConfigProvider;
import net.william278.husksync.config.Locales; import net.william278.husksync.config.Locales;
import net.william278.husksync.config.Server; import net.william278.husksync.config.Server;
import net.william278.husksync.config.Settings; import net.william278.husksync.config.Settings;
@@ -47,9 +47,7 @@ import net.william278.husksync.util.Task;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.*; import java.util.*;
import java.util.logging.Level; import java.util.logging.Level;
@@ -57,7 +55,7 @@ import java.util.logging.Level;
/** /**
* Abstract implementation of the HuskSync plugin. * Abstract implementation of the HuskSync plugin.
*/ */
public interface HuskSync extends Task.Supplier, EventDispatcher { public interface HuskSync extends Task.Supplier, EventDispatcher, ConfigProvider {
int SPIGOT_RESOURCE_ID = 97144; int SPIGOT_RESOURCE_ID = 97144;
@@ -195,7 +193,7 @@ public interface HuskSync extends Task.Supplier, EventDispatcher {
@NotNull @NotNull
String getServerName(); String getServerName();
void setServer(@NotNull Server server); void setServerName(@NotNull Server serverName);
/** /**
* Returns the plugin {@link Locales} * Returns the plugin {@link Locales}
@@ -247,7 +245,7 @@ public interface HuskSync extends Task.Supplier, EventDispatcher {
* @param throwable a throwable to log * @param throwable a throwable to log
*/ */
default void debug(@NotNull String message, @NotNull Throwable... throwable) { default void debug(@NotNull String message, @NotNull Throwable... throwable) {
if (getSettings().doDebugLogging()) { if (getSettings().isDebugLogging()) {
log(Level.INFO, getDebugString(message), throwable); log(Level.INFO, getDebugString(message), throwable);
} }
} }
@@ -320,37 +318,6 @@ public interface HuskSync extends Task.Supplier, EventDispatcher {
*/ */
Optional<LegacyConverter> getLegacyConverter(); Optional<LegacyConverter> getLegacyConverter();
/**
* Reloads the {@link Settings} and {@link Locales} from their respective config files.
*/
default void loadConfigs() {
try {
// Load settings
setSettings(Annotaml.create(
new File(getDataFolder(), "config.yml"),
Settings.class
).get());
// Load server name
setServer(Annotaml.create(
new File(getDataFolder(), "server.yml"),
Server.getDefault(this)
).get());
// Load locales from language preset default
final Locales languagePresets = Annotaml.create(
Locales.class,
Objects.requireNonNull(getResource(String.format("locales/%s.yml", getSettings().getLanguage())))
).get();
setLocales(Annotaml.create(new File(
getDataFolder(),
String.format("messages_%s.yml", getSettings().getLanguage())
), languagePresets).get());
} catch (IOException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
throw new FailedToLoadException("Failed to load config or message files", e);
}
}
@NotNull @NotNull
default UpdateChecker getUpdateChecker() { default UpdateChecker getUpdateChecker() {
return UpdateChecker.builder() return UpdateChecker.builder()
@@ -361,7 +328,7 @@ public interface HuskSync extends Task.Supplier, EventDispatcher {
} }
default void checkForUpdates() { default void checkForUpdates() {
if (getSettings().doCheckForUpdates()) { if (getSettings().isCheckForUpdates()) {
getUpdateChecker().check().thenAccept(checked -> { getUpdateChecker().check().thenAccept(checked -> {
if (!checked.isUpToDate()) { if (!checked.isUpToDate()) {
log(Level.WARNING, String.format( log(Level.WARNING, String.format(
@@ -414,7 +381,7 @@ public interface HuskSync extends Task.Supplier, EventDispatcher {
1) Make sure you've entered your MySQL or MariaDB database details correctly in config.yml 1) Make sure you've entered your MySQL or MariaDB database details correctly in config.yml
2) Make sure your Redis server details are also correct in config.yml 2) Make sure your Redis server details are also correct in config.yml
3) Make sure your config is up-to-date (https://william278.net/docs/husksync/config-files) 3) Make sure your config is up-to-date (https://william278.net/docs/husksync/config-file)
4) Check the error below for more details 4) Check the error below for more details
Caused by: %s"""; Caused by: %s""";

View File

@@ -38,14 +38,17 @@ import java.util.UUID;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
/** /**
* The base implementation of the HuskSync API, containing cross-platform API calls. * The common implementation of the HuskSync API, containing cross-platform API calls.
* </p> * </p>
* This class should not be used directly, but rather through platform-specific extending API classes. * Retrieve an instance of the API class via {@link #getInstance()}.
* *
* @since 2.0 * @since 2.0
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
public abstract class HuskSyncAPI { public class HuskSyncAPI {
// Instance of the plugin
protected static HuskSyncAPI instance;
/** /**
* <b>(Internal use only)</b> - Instance of the implementing plugin. * <b>(Internal use only)</b> - Instance of the implementing plugin.
@@ -60,6 +63,28 @@ public abstract class HuskSyncAPI {
this.plugin = plugin; this.plugin = plugin;
} }
/**
* Entrypoint to the HuskSync API on the common platform - returns an instance of the API
*
* @return instance of the HuskSync API
* @since 3.3
*/
@NotNull
public static HuskSyncAPI getInstance() {
if (instance == null) {
throw new NotRegisteredException();
}
return instance;
}
/**
* <b>(Internal use only)</b> - Unregister the API for this platform.
*/
@ApiStatus.Internal
public static void unregister() {
instance = null;
}
/** /**
* Get a {@link User} by their UUID * Get a {@link User} by their UUID
* *

View File

@@ -19,11 +19,11 @@
package net.william278.husksync.command; package net.william278.husksync.command;
import com.google.common.collect.Maps;
import net.william278.husksync.HuskSync; import net.william278.husksync.HuskSync;
import net.william278.husksync.user.CommandUser; import net.william278.husksync.user.CommandUser;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -36,7 +36,7 @@ public abstract class Command extends Node {
@NotNull HuskSync plugin) { @NotNull HuskSync plugin) {
super(name, aliases, plugin); super(name, aliases, plugin);
this.usage = usage; this.usage = usage;
this.additionalPermissions = new HashMap<>(); this.additionalPermissions = Maps.newHashMap();
} }
@Override @Override

View File

@@ -81,9 +81,11 @@ public class EnderChestCommand extends ItemsCommand {
// Create and pack the snapshot with the updated enderChest // Create and pack the snapshot with the updated enderChest
final DataSnapshot.Packed snapshot = latestData.get().copy(); final DataSnapshot.Packed snapshot = latestData.get().copy();
snapshot.edit(plugin, (data) -> { snapshot.edit(plugin, (data) -> {
data.setSaveCause(DataSnapshot.SaveCause.ENDERCHEST_COMMAND);
data.setPinned(plugin.getSettings().doAutoPin(DataSnapshot.SaveCause.ENDERCHEST_COMMAND));
data.getEnderChest().ifPresent(enderChest -> enderChest.setContents(items)); data.getEnderChest().ifPresent(enderChest -> enderChest.setContents(items));
data.setSaveCause(DataSnapshot.SaveCause.ENDERCHEST_COMMAND);
data.setPinned(
plugin.getSettings().getSynchronization().doAutoPin(DataSnapshot.SaveCause.ENDERCHEST_COMMAND)
);
}); });
plugin.getDatabase().addSnapshot(user, snapshot); plugin.getDatabase().addSnapshot(user, snapshot);
plugin.getRedisManager().sendUserDataUpdate(user, snapshot); plugin.getRedisManager().sendUserDataUpdate(user, snapshot);

View File

@@ -79,7 +79,9 @@ public class HuskSyncCommand extends Command implements TabProvider {
AboutMenu.Credit.of("DJelly4K").description("Simplified Chinese (zh-cn)"), AboutMenu.Credit.of("DJelly4K").description("Simplified Chinese (zh-cn)"),
AboutMenu.Credit.of("Thourgard").description("Ukrainian (uk-ua)"), AboutMenu.Credit.of("Thourgard").description("Ukrainian (uk-ua)"),
AboutMenu.Credit.of("xF3d3").description("Italian (it-it)"), AboutMenu.Credit.of("xF3d3").description("Italian (it-it)"),
AboutMenu.Credit.of("cada3141").description("Korean (ko-kr)")) AboutMenu.Credit.of("cada3141").description("Korean (ko-kr)"),
AboutMenu.Credit.of("Wirayuda5620").description("Indonesian (id-id)"),
AboutMenu.Credit.of("WinTone01").description("Turkish (tr-tr)"))
.buttons( .buttons(
AboutMenu.Link.of("https://william278.net/docs/husksync").text("Documentation").icon(""), AboutMenu.Link.of("https://william278.net/docs/husksync").text("Documentation").icon(""),
AboutMenu.Link.of("https://github.com/WiIIiam278/HuskSync/issues").text("Issues").icon("").color(TextColor.color(0xff9f0f)), AboutMenu.Link.of("https://github.com/WiIIiam278/HuskSync/issues").text("Issues").icon("").color(TextColor.color(0xff9f0f)),
@@ -107,7 +109,9 @@ public class HuskSyncCommand extends Command implements TabProvider {
} }
case "reload" -> { case "reload" -> {
try { try {
plugin.loadConfigs(); plugin.loadSettings();
plugin.loadLocales();
plugin.loadServer();
plugin.getLocales().getLocale("reload_complete").ifPresent(executor::sendMessage); plugin.getLocales().getLocale("reload_complete").ifPresent(executor::sendMessage);
} catch (Throwable e) { } catch (Throwable e) {
executor.sendMessage(new MineDown( executor.sendMessage(new MineDown(
@@ -204,19 +208,31 @@ public class HuskSyncCommand extends Command implements TabProvider {
MINECRAFT_VERSION(plugin -> Component.text(plugin.getMinecraftVersion().toString())), MINECRAFT_VERSION(plugin -> Component.text(plugin.getMinecraftVersion().toString())),
JAVA_VERSION(plugin -> Component.text(System.getProperty("java.version"))), JAVA_VERSION(plugin -> Component.text(System.getProperty("java.version"))),
JAVA_VENDOR(plugin -> Component.text(System.getProperty("java.vendor"))), JAVA_VENDOR(plugin -> Component.text(System.getProperty("java.vendor"))),
SYNC_MODE(plugin -> Component.text(WordUtils.capitalizeFully(plugin.getSettings().getSyncMode().toString()))), SYNC_MODE(plugin -> Component.text(WordUtils.capitalizeFully(
DELAY_LATENCY(plugin -> Component.text(plugin.getSettings().getNetworkLatencyMilliseconds() + "ms")), plugin.getSettings().getSynchronization().getMode().toString()
))),
DELAY_LATENCY(plugin -> Component.text(
plugin.getSettings().getSynchronization().getNetworkLatencyMilliseconds() + "ms"
)),
SERVER_NAME(plugin -> Component.text(plugin.getServerName())), SERVER_NAME(plugin -> Component.text(plugin.getServerName())),
DATABASE_TYPE(plugin -> Component.text(plugin.getSettings().getDatabaseType().getDisplayName())), DATABASE_TYPE(plugin -> Component.text(plugin.getSettings().getDatabase().getType().getDisplayName())),
IS_DATABASE_LOCAL(plugin -> getLocalhostBoolean(plugin.getSettings().getMySqlHost())), IS_DATABASE_LOCAL(plugin -> getLocalhostBoolean(plugin.getSettings().getDatabase().getCredentials().getHost())),
USING_REDIS_SENTINEL(plugin -> getBoolean(!plugin.getSettings().getRedisSentinelMaster().isBlank())), USING_REDIS_SENTINEL(plugin -> getBoolean(
USING_REDIS_PASSWORD(plugin -> getBoolean(!plugin.getSettings().getRedisPassword().isBlank())), !plugin.getSettings().getRedis().getSentinel().getMaster().isBlank()
REDIS_USING_SSL(plugin -> getBoolean(plugin.getSettings().redisUseSsl())), )),
IS_REDIS_LOCAL(plugin -> getLocalhostBoolean(plugin.getSettings().getRedisHost())), USING_REDIS_PASSWORD(plugin -> getBoolean(
!plugin.getSettings().getRedis().getCredentials().getPassword().isBlank()
)),
REDIS_USING_SSL(plugin -> getBoolean(
plugin.getSettings().getRedis().getCredentials().isUseSsl()
)),
IS_REDIS_LOCAL(plugin -> getLocalhostBoolean(
plugin.getSettings().getRedis().getCredentials().getHost()
)),
DATA_TYPES(plugin -> Component.join( DATA_TYPES(plugin -> Component.join(
JoinConfiguration.commas(true), JoinConfiguration.commas(true),
plugin.getRegisteredDataTypes().stream().map(i -> { plugin.getRegisteredDataTypes().stream().map(i -> {
boolean enabled = plugin.getSettings().isSyncFeatureEnabled(i); boolean enabled = plugin.getSettings().getSynchronization().isFeatureEnabled(i);
return Component.textOfChildren(Component return Component.textOfChildren(Component
.text(i.toString()).appendSpace().append(Component.text(enabled ? '✔' : '❌'))) .text(i.toString()).appendSpace().append(Component.text(enabled ? '✔' : '❌')))
.color(enabled ? NamedTextColor.GREEN : NamedTextColor.RED) .color(enabled ? NamedTextColor.GREEN : NamedTextColor.RED)

View File

@@ -81,9 +81,11 @@ public class InventoryCommand extends ItemsCommand {
// Create and pack the snapshot with the updated inventory // Create and pack the snapshot with the updated inventory
final DataSnapshot.Packed snapshot = latestData.get().copy(); final DataSnapshot.Packed snapshot = latestData.get().copy();
snapshot.edit(plugin, (data) -> { snapshot.edit(plugin, (data) -> {
data.setSaveCause(DataSnapshot.SaveCause.INVENTORY_COMMAND);
data.setPinned(plugin.getSettings().doAutoPin(DataSnapshot.SaveCause.INVENTORY_COMMAND));
data.getInventory().ifPresent(inventory -> inventory.setContents(items)); data.getInventory().ifPresent(inventory -> inventory.setContents(items));
data.setSaveCause(DataSnapshot.SaveCause.INVENTORY_COMMAND);
data.setPinned(
plugin.getSettings().getSynchronization().doAutoPin(DataSnapshot.SaveCause.INVENTORY_COMMAND)
);
}); });
plugin.getDatabase().addSnapshot(user, snapshot); plugin.getDatabase().addSnapshot(user, snapshot);
plugin.getRedisManager().sendUserDataUpdate(user, snapshot); plugin.getRedisManager().sendUserDataUpdate(user, snapshot);

View File

@@ -147,7 +147,9 @@ public class UserDataCommand extends Command implements TabProvider {
data.edit(plugin, (unpacked -> { data.edit(plugin, (unpacked -> {
unpacked.getHealth().ifPresent(status -> status.setHealth(Math.max(1, status.getHealth()))); unpacked.getHealth().ifPresent(status -> status.setHealth(Math.max(1, status.getHealth())));
unpacked.setSaveCause(DataSnapshot.SaveCause.BACKUP_RESTORE); unpacked.setSaveCause(DataSnapshot.SaveCause.BACKUP_RESTORE);
unpacked.setPinned(plugin.getSettings().doAutoPin(DataSnapshot.SaveCause.BACKUP_RESTORE)); unpacked.setPinned(
plugin.getSettings().getSynchronization().doAutoPin(DataSnapshot.SaveCause.BACKUP_RESTORE)
);
})); }));
// Set the user's data and send a message // Set the user's data and send a message

View File

@@ -0,0 +1,155 @@
/*
* This file is part of HuskSync, licensed under the Apache License 2.0.
*
* Copyright (c) William278 <will27528@gmail.com>
* 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.config;
import de.exlll.configlib.NameFormatters;
import de.exlll.configlib.YamlConfigurationProperties;
import de.exlll.configlib.YamlConfigurationStore;
import de.exlll.configlib.YamlConfigurations;
import net.william278.husksync.HuskSync;
import org.jetbrains.annotations.NotNull;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.logging.Level;
/**
* Interface for getting and setting data from plugin configuration files
*
* @since 1.0
*/
public interface ConfigProvider {
@NotNull
YamlConfigurationProperties.Builder<?> YAML_CONFIGURATION_PROPERTIES = YamlConfigurationProperties.newBuilder()
.charset(StandardCharsets.UTF_8)
.setNameFormatter(NameFormatters.LOWER_UNDERSCORE);
/**
* Get the plugin settings, read from the config file
*
* @return the plugin settings
* @since 1.0
*/
@NotNull
Settings getSettings();
/**
* Set the plugin settings
*
* @param settings The settings to set
* @since 1.0
*/
void setSettings(@NotNull Settings settings);
/**
* Load the plugin settings from the config file
*
* @since 1.0
*/
default void loadSettings() {
setSettings(YamlConfigurations.update(
getConfigDirectory().resolve("config.yml"),
Settings.class,
YAML_CONFIGURATION_PROPERTIES.header(Settings.CONFIG_HEADER).build()
));
}
/**
* Get the locales for the plugin
*
* @return the locales for the plugin
* @since 1.0
*/
@NotNull
Locales getLocales();
/**
* Set the locales for the plugin
*
* @param locales The locales to set
* @since 1.0
*/
void setLocales(@NotNull Locales locales);
/**
* Load the locales from the config file
*
* @since 1.0
*/
default void loadLocales() {
final YamlConfigurationStore<Locales> store = new YamlConfigurationStore<>(
Locales.class, YAML_CONFIGURATION_PROPERTIES.header(Locales.CONFIG_HEADER).build()
);
// Read existing locales if present
final Path path = getConfigDirectory().resolve(String.format("messages-%s.yml", getSettings().getLanguage()));
if (Files.exists(path)) {
setLocales(store.load(path));
return;
}
// Otherwise, save and read the default locales
try (InputStream input = getResource(String.format("locales/%s.yml", getSettings().getLanguage()))) {
final Locales locales = store.read(input);
store.save(locales, path);
setLocales(locales);
} catch (Throwable e) {
getPlugin().log(Level.SEVERE, "An error occurred loading the locales (invalid lang code?)", e);
}
}
@NotNull
String getServerName();
void setServerName(@NotNull Server server);
default void loadServer() {
setServerName(YamlConfigurations.update(
getConfigDirectory().resolve("server.yml"),
Server.class,
YAML_CONFIGURATION_PROPERTIES.header(Server.CONFIG_HEADER).build()
));
}
/**
* Get a plugin resource
*
* @param name The name of the resource
* @return the resource, if found
* @since 1.0
*/
InputStream getResource(@NotNull String name);
/**
* Get the plugin config directory
*
* @return the plugin config directory
* @since 1.0
*/
@NotNull
Path getConfigDirectory();
@NotNull
HuskSync getPlugin();
}

View File

@@ -19,53 +19,60 @@
package net.william278.husksync.config; package net.william278.husksync.config;
import com.google.common.collect.Maps;
import de.exlll.configlib.Configuration;
import de.themoep.minedown.adventure.MineDown; import de.themoep.minedown.adventure.MineDown;
import net.william278.annotaml.YamlFile; import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import net.william278.paginedown.ListOptions; import net.william278.paginedown.ListOptions;
import org.apache.commons.text.StringEscapeUtils; import org.apache.commons.text.StringEscapeUtils;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
/** /**
* Loaded locales used by the plugin to display styled messages * Plugin locale configuration
*
* @since 1.0
*/ */
@YamlFile(rootedMap = true, header = """ @SuppressWarnings("FieldMayBeFinal")
@Configuration
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class Locales {
static final String CONFIG_HEADER = """
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
HuskSync Locales ┃ HuskSync - Locales ┃
┃ Developed by William278 ┃ ┃ Developed by William278 ┃
┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
┣╸ See plugin about menu for international locale credits ┣╸ See plugin about menu for international locale credits
┣╸ Formatted in MineDown: https://github.com/Phoenix616/MineDown ┣╸ Formatted in MineDown: https://github.com/Phoenix616/MineDown
┗╸ Translate HuskSync: https://william278.net/docs/husksync/translations""") ┗╸ Translate HuskSync: https://william278.net/docs/husksync/translations""";
public class Locales {
protected static final String DEFAULT_LOCALE = "en-gb";
// The raw set of locales loaded from yaml
Map<String, String> locales = Maps.newTreeMap();
/** /**
* The raw set of locales loaded from yaml * Returns a raw, un-formatted locale loaded from the locales file
*/
@NotNull
public Map<String, String> rawLocales = new HashMap<>();
/**
* Returns a raw, unformatted locale loaded from the Locales file
* *
* @param localeId String identifier of the locale, corresponding to a key in the file * @param localeId String identifier of the locale, corresponding to a key in the file
* @return An {@link Optional} containing the locale corresponding to the id, if it exists * @return An {@link Optional} containing the locale corresponding to the id, if it exists
*/ */
public Optional<String> getRawLocale(@NotNull String localeId) { public Optional<String> getRawLocale(@NotNull String localeId) {
return Optional.ofNullable(rawLocales.get(localeId)).map(StringEscapeUtils::unescapeJava); return Optional.ofNullable(locales.get(localeId)).map(StringEscapeUtils::unescapeJava);
} }
/** /**
* Returns a raw, unformatted locale loaded from the Locales file, with replacements applied * Returns a raw, un-formatted locale loaded from the locales file, with replacements applied
* <p> * <p>
* Note that replacements will not be MineDown-escaped; use {@link #escapeMineDown(String)} to escape replacements * Note that replacements will not be MineDown-escaped; use {@link #escapeText(String)} to escape replacements
* *
* @param localeId String identifier of the locale, corresponding to a key in the file * @param localeId String identifier of the locale, corresponding to a key in the file
* @param replacements An ordered array of replacement strings to fill in placeholders with * @param replacements Ordered array of replacement strings to fill in placeholders with
* @return An {@link Optional} containing the replacement-applied locale corresponding to the id, if it exists * @return An {@link Optional} containing the replacement-applied locale corresponding to the id, if it exists
*/ */
public Optional<String> getRawLocale(@NotNull String localeId, @NotNull String... replacements) { public Optional<String> getRawLocale(@NotNull String localeId, @NotNull String... replacements) {
@@ -73,34 +80,45 @@ public class Locales {
} }
/** /**
* Returns a MineDown-formatted locale from the Locales file * Returns a MineDown-formatted locale from the locales file
* *
* @param localeId String identifier of the locale, corresponding to a key in the file * @param localeId String identifier of the locale, corresponding to a key in the file
* @return An {@link Optional} containing the formatted locale corresponding to the id, if it exists * @return An {@link Optional} containing the formatted locale corresponding to the id, if it exists
*/ */
public Optional<MineDown> getLocale(@NotNull String localeId) { public Optional<MineDown> getLocale(@NotNull String localeId) {
return getRawLocale(localeId).map(MineDown::new); return getRawLocale(localeId).map(this::format);
} }
/** /**
* Returns a MineDown-formatted locale from the Locales file, with replacements applied * Returns a MineDown-formatted locale from the locales file, with replacements applied
* <p> * <p>
* Note that replacements will be MineDown-escaped before application * Note that replacements will be MineDown-escaped before application
* *
* @param localeId String identifier of the locale, corresponding to a key in the file * @param localeId String identifier of the locale, corresponding to a key in the file
* @param replacements An ordered array of replacement strings to fill in placeholders with * @param replacements Ordered array of replacement strings to fill in placeholders with
* @return An {@link Optional} containing the replacement-applied, formatted locale corresponding to the id, if it exists * @return An {@link Optional} containing the replacement-applied, formatted locale corresponding to the id, if it exists
*/ */
public Optional<MineDown> getLocale(@NotNull String localeId, @NotNull String... replacements) { public Optional<MineDown> getLocale(@NotNull String localeId, @NotNull String... replacements) {
return getRawLocale(localeId, Arrays.stream(replacements).map(Locales::escapeMineDown) return getRawLocale(localeId, Arrays.stream(replacements).map(Locales::escapeText)
.toArray(String[]::new)).map(MineDown::new); .toArray(String[]::new)).map(this::format);
}
/**
* Returns a MineDown-formatted string
*
* @param text The text to format
* @return A {@link MineDown} object containing the formatted text
*/
@NotNull
public MineDown format(@NotNull String text) {
return new MineDown(text);
} }
/** /**
* Apply placeholder replacements to a raw locale * Apply placeholder replacements to a raw locale
* *
* @param rawLocale The raw, unparsed locale * @param rawLocale The raw, unparsed locale
* @param replacements An ordered array of replacement strings to fill in placeholders with * @param replacements Ordered array of replacement strings to fill in placeholders with
* @return the raw locale, with inserted placeholders * @return the raw locale, with inserted placeholders
*/ */
@NotNull @NotNull
@@ -116,15 +134,12 @@ public class Locales {
/** /**
* Escape a string from {@link MineDown} formatting for use in a MineDown-formatted locale * Escape a string from {@link MineDown} formatting for use in a MineDown-formatted locale
* <p>
* Although MineDown provides {@link MineDown#escape(String)}, that method fails to escape events
* properly when using the escaped string in a replacement, so this is used instead
* *
* @param string The string to escape * @param string The string to escape
* @return The escaped string * @return The escaped string
*/ */
@NotNull @NotNull
public static String escapeMineDown(@NotNull String string) { public static String escapeText(@NotNull String string) {
final StringBuilder value = new StringBuilder(); final StringBuilder value = new StringBuilder();
for (int i = 0; i < string.length(); ++i) { for (int i = 0; i < string.length(); ++i) {
char c = string.charAt(i); char c = string.charAt(i);
@@ -137,22 +152,7 @@ public class Locales {
value.append(c); value.append(c);
} }
return value.toString(); return value.toString().replace("__", "_\\_");
}
/**
* Truncates a String to a specified length, and appends an ellipsis if it is longer than the specified length
*
* @param string The string to truncate
* @param length The maximum length of the string
* @return The truncated string
*/
@NotNull
public static String truncate(@NotNull String string, int length) {
if (string.length() > length) {
return string.substring(0, length) + "";
}
return string;
} }
/** /**
@@ -185,10 +185,6 @@ public class Locales {
.setSpaceBeforeFooter(false); .setSpaceBeforeFooter(false);
} }
@SuppressWarnings("unused")
public Locales() {
}
/** /**
* Determines the slot a system notification should be displayed in * Determines the slot a system notification should be displayed in
*/ */

View File

@@ -19,64 +19,43 @@
package net.william278.husksync.config; package net.william278.husksync.config;
import net.william278.annotaml.Annotaml; import de.exlll.configlib.Configuration;
import net.william278.annotaml.YamlFile; import lombok.AccessLevel;
import net.william278.annotaml.YamlKey; import lombok.AllArgsConstructor;
import net.william278.husksync.HuskSync; import lombok.Getter;
import lombok.NoArgsConstructor;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.List;
/** @Getter
* Represents a server on a proxied network. @Configuration
*/ @NoArgsConstructor(access = AccessLevel.PRIVATE)
@YamlFile(header = """ @AllArgsConstructor(access = AccessLevel.PRIVATE)
public class Server {
static final String CONFIG_HEADER = """
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
HuskSync Server ID config HuskSync - Server ID
┃ Developed by William278 ┃ ┃ Developed by William278 ┃
┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
┣╸ This file should contain the ID of this server as defined in your proxy config. ┣╸ This file should contain the ID of this server as defined in your proxy config.
┗╸ If you join it using /server alpha, then set it to 'alpha' (case-sensitive)""") ┗╸ If you join it using /server alpha, then set it to 'alpha' (case-sensitive)""";
public class Server {
@YamlKey("name") private String name = getDefault();
private String serverName;
private Server(@NotNull String serverName) {
this.serverName = serverName;
}
@SuppressWarnings("unused")
private Server() {
}
@NotNull @NotNull
public static Server getDefault(@NotNull HuskSync plugin) { public static Server of(@NotNull String name) {
return new Server(getDefaultServerName(plugin)); return new Server(name);
} }
/** /**
* Find a sensible default name for the server name property * Find a sensible default name for the server name property
*/ */
@NotNull @NotNull
private static String getDefaultServerName(@NotNull HuskSync plugin) { private static String getDefault() {
try { final String serverFolder = System.getProperty("user.dir");
// Fetch server default from supported plugins if present return serverFolder == null ? "server" : Path.of(serverFolder).getFileName().toString().trim();
for (String s : List.of("HuskHomes", "HuskTowns")) {
final File serverFile = Path.of(plugin.getDataFolder().getParent(), s, "server.yml").toFile();
if (serverFile.exists()) {
return Annotaml.create(serverFile, Server.class).get().getName();
}
}
// Fetch server default from user dir name
final Path serverDirectory = Path.of(System.getProperty("user.dir"));
return serverDirectory.getFileName().toString().trim();
} catch (Throwable e) {
return "server";
}
} }
@Override @Override
@@ -88,12 +67,4 @@ public class Server {
return super.equals(other); return super.equals(other);
} }
/**
* Proxy-defined name of this server.
*/
@NotNull
public String getName() {
return serverName;
}
} }

View File

@@ -19,9 +19,12 @@
package net.william278.husksync.config; package net.william278.husksync.config;
import net.william278.annotaml.YamlComment; import com.google.common.collect.Lists;
import net.william278.annotaml.YamlFile; import de.exlll.configlib.Comment;
import net.william278.annotaml.YamlKey; import de.exlll.configlib.Configuration;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import net.william278.husksync.data.DataSnapshot; import net.william278.husksync.data.DataSnapshot;
import net.william278.husksync.data.Identifier; import net.william278.husksync.data.Identifier;
import net.william278.husksync.database.Database; import net.william278.husksync.database.Database;
@@ -29,140 +32,163 @@ import net.william278.husksync.listener.EventListener;
import net.william278.husksync.sync.DataSyncer; import net.william278.husksync.sync.DataSyncer;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.*; import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
/** /**
* Plugin settings, read from config.yml * Plugin settings, read from config.yml
*/ */
@YamlFile(header = """ @SuppressWarnings("FieldMayBeFinal")
@Getter
@Configuration
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class Settings {
protected static final String CONFIG_HEADER = """
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ HuskSync Config ┃ ┃ HuskSync Config ┃
┃ Developed by William278 ┃ ┃ Developed by William278 ┃
┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
┣╸ Information: https://william278.net/project/husksync ┣╸ Information: https://william278.net/project/husksync
┣╸ Config Help: https://william278.net/docs/husksync/config-file/ ┣╸ Config Help: https://william278.net/docs/husksync/config-file/
┗╸ Documentation: https://william278.net/docs/husksync""") ┗╸ Documentation: https://william278.net/docs/husksync""";
public class Settings {
// Top-level settings // Top-level settings
@YamlComment("Locale of the default language file to use. Docs: https://william278.net/docs/husksync/translations") @Comment({"Locale of the default language file to use.", "Docs: https://william278.net/docs/husksync/translations"})
@YamlKey("language") private String language = Locales.DEFAULT_LOCALE;
private String language = "en-gb";
@YamlComment("Whether to automatically check for plugin updates on startup") @Comment("Whether to automatically check for plugin updates on startup")
@YamlKey("check_for_updates")
private boolean checkForUpdates = true; private boolean checkForUpdates = true;
@YamlComment("Specify a common ID for grouping servers running HuskSync. " @Comment("Specify a common ID for grouping servers running HuskSync. "
+ "Don't modify this unless you know what you're doing!") + "Don't modify this unless you know what you're doing!")
@YamlKey("cluster_id")
private String clusterId = ""; private String clusterId = "";
@YamlComment("Enable development debug logging") @Comment("Enable development debug logging")
@YamlKey("debug_logging")
private boolean debugLogging = false; private boolean debugLogging = false;
@YamlComment("Whether to provide modern, rich TAB suggestions for commands (if available)") @Comment("Whether to provide modern, rich TAB suggestions for commands (if available)")
@YamlKey("brigadier_tab_completion")
private boolean brigadierTabCompletion = false; private boolean brigadierTabCompletion = false;
@YamlComment("Whether to enable the Player Analytics hook. Docs: https://william278.net/docs/husksync/plan-hook") @Comment({"Whether to enable the Player Analytics hook.", "Docs: https://william278.net/docs/husksync/plan-hook"})
@YamlKey("enable_plan_hook")
private boolean enablePlanHook = true; private boolean enablePlanHook = true;
// Database settings // Database settings
@YamlComment("Type of database to use (MYSQL, MARIADB)") @Comment("Database settings")
@YamlKey("database.type") private DatabaseSettings database = new DatabaseSettings();
private Database.Type databaseType = Database.Type.MYSQL;
@YamlComment("Specify credentials here for your MYSQL or MARIADB database") @Getter
@YamlKey("database.credentials.host") @Configuration
private String mySqlHost = "localhost"; @NoArgsConstructor(access = AccessLevel.PRIVATE)
public static class DatabaseSettings {
@YamlKey("database.credentials.port") @Comment("Type of database to use (MYSQL, MARIADB)")
private int mySqlPort = 3306; private Database.Type type = Database.Type.MYSQL;
@YamlKey("database.credentials.database") @Comment("Specify credentials here for your MYSQL or MARIADB database")
private String mySqlDatabase = "HuskSync"; private DatabaseCredentials credentials = new DatabaseCredentials();
@YamlKey("database.credentials.username") @Getter
private String mySqlUsername = "root"; @Configuration
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public static class DatabaseCredentials {
private String host = "localhost";
private int port = 3306;
private String database = "HuskSync";
private String username = "root";
private String password = "pa55w0rd";
private String parameters = String.join("&",
"?autoReconnect=true", "useSSL=false",
"useUnicode=true", "characterEncoding=UTF-8");
}
@YamlKey("database.credentials.password") @Comment("MYSQL / MARIADB database Hikari connection pool properties. Don't modify this unless you know what you're doing!")
private String mySqlPassword = "pa55w0rd"; private PoolSettings connectionPool = new PoolSettings();
@YamlKey("database.credentials.parameters") @Getter
private String mySqlConnectionParameters = "?autoReconnect=true" @Configuration
+ "&useSSL=false" @NoArgsConstructor(access = AccessLevel.PRIVATE)
+ "&useUnicode=true" public static class PoolSettings {
+ "&characterEncoding=UTF-8"; private int maximumPoolSize = 10;
private int minimumIdle = 10;
private long maximumLifetime = 1800000;
private long keepaliveTime = 0;
private long connectionTimeout = 5000;
}
@YamlComment("MYSQL / MARIADB database Hikari connection pool properties. " @Comment("Names of tables to use on your database. Don't modify this unless you know what you're doing!")
+ "Don't modify this unless you know what you're doing!") @Getter(AccessLevel.NONE)
@YamlKey("database.connection_pool.maximum_pool_size") private Map<String, String> tableNames = Database.TableName.getDefaults();
private int mySqlConnectionPoolSize = 10;
@YamlKey("database.connection_pool.minimum_idle")
private int mySqlConnectionPoolIdle = 10;
@YamlKey("database.connection_pool.maximum_lifetime")
private long mySqlConnectionPoolLifetime = 1800000;
@YamlKey("database.connection_pool.keepalive_time")
private long mySqlConnectionPoolKeepAlive = 0;
@YamlKey("database.connection_pool.connection_timeout")
private long mySqlConnectionPoolTimeout = 5000;
@YamlComment("Names of tables to use on your database. Don't modify this unless you know what you're doing!")
@YamlKey("database.table_names")
private Map<String, String> tableNames = TableName.getDefaults();
@NotNull
public String getTableName(@NotNull Database.TableName tableName) {
return tableNames.getOrDefault(tableName.name().toLowerCase(Locale.ENGLISH), tableName.getDefaultName());
}
}
// Redis settings // Redis settings
@YamlComment("Specify the credentials of your Redis database here. Set \"password\" to '' if you don't have one") @Comment("Redis settings")
@YamlKey("redis.credentials.host") private RedisSettings redis = new RedisSettings();
private String redisHost = "localhost";
@YamlKey("redis.credentials.port") @Getter
private int redisPort = 6379; @Configuration
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public static class RedisSettings {
@YamlKey("redis.credentials.password") @Comment("Specify the credentials of your Redis database here. Set \"password\" to '' if you don't have one")
private String redisPassword = ""; private RedisCredentials credentials = new RedisCredentials();
@YamlKey("redis.use_ssl") @Getter
private boolean redisUseSsl = false; @Configuration
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public static class RedisCredentials {
private String host = "localhost";
private int port = 6379;
private String password = "";
private boolean useSsl = false;
}
@YamlComment("If you're using Redis Sentinel, specify the master set name. If you don't know what this is, don't change anything here.") @Comment("Options for if you're using Redis sentinel. Don't modify this unless you know what you're doing!")
@YamlKey("redis.sentinel.master") private RedisSentinel sentinel = new RedisSentinel();
private String redisSentinelMaster = "";
@YamlComment("List of host:port pairs") @Getter
@YamlKey("redis.sentinel.nodes") @Configuration
private List<String> redisSentinelNodes = new ArrayList<>(); @NoArgsConstructor(access = AccessLevel.PRIVATE)
public static class RedisSentinel {
@YamlKey("redis.sentinel.password") @Comment("The master set name for the Redis sentinel.")
private String redisSentinelPassword = ""; private String master = "";
@Comment("List of host:port pairs")
private List<String> nodes = Lists.newArrayList();
private String password = "";
}
}
// Synchronization settings // Synchronization settings
@YamlComment("The data synchronization mode to use (LOCKSTEP or DELAY). LOCKSTEP is recommended for most networks." @Comment("Redis settings")
+ " Docs: https://william278.net/docs/husksync/sync-modes") private SynchronizationSettings synchronization = new SynchronizationSettings();
@YamlKey("synchronization.mode")
private DataSyncer.Mode syncMode = DataSyncer.Mode.LOCKSTEP;
@YamlComment("The number of data snapshot backups that should be kept at once per user") @Getter
@YamlKey("synchronization.max_user_data_snapshots") @Configuration
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public static class SynchronizationSettings {
@Comment({"The data synchronization mode to use (LOCKSTEP or DELAY). LOCKSTEP is recommended for most networks.",
"Docs: https://william278.net/docs/husksync/sync-modes"})
private DataSyncer.Mode mode = DataSyncer.Mode.LOCKSTEP;
@Comment("The number of data snapshot backups that should be kept at once per user")
private int maxUserDataSnapshots = 16; private int maxUserDataSnapshots = 16;
@YamlComment("Number of hours between new snapshots being saved as backups (Use \"0\" to backup all snapshots)") @Comment("Number of hours between new snapshots being saved as backups (Use \"0\" to backup all snapshots)")
@YamlKey("synchronization.snapshot_backup_frequency")
private int snapshotBackupFrequency = 4; private int snapshotBackupFrequency = 4;
@YamlComment("List of save cause IDs for which a snapshot will be automatically pinned (so it won't be rotated)." @Comment({"List of save cause IDs for which a snapshot will be automatically pinned (so it won't be rotated).",
+ " Docs: https://william278.net/docs/husksync/data-rotation#save-causes") "Docs: https://william278.net/docs/husksync/data-rotation#save-causes"})
@YamlKey("synchronization.auto_pinned_save_causes") @Getter(AccessLevel.NONE)
private List<String> autoPinnedSaveCauses = List.of( private List<String> autoPinnedSaveCauses = List.of(
DataSnapshot.SaveCause.INVENTORY_COMMAND.name(), DataSnapshot.SaveCause.INVENTORY_COMMAND.name(),
DataSnapshot.SaveCause.ENDERCHEST_COMMAND.name(), DataSnapshot.SaveCause.ENDERCHEST_COMMAND.name(),
@@ -171,266 +197,28 @@ public class Settings {
DataSnapshot.SaveCause.MPDB_MIGRATION.name() DataSnapshot.SaveCause.MPDB_MIGRATION.name()
); );
@YamlComment("Whether to create a snapshot for users on a world when the server saves that world") @Comment("Whether to create a snapshot for users on a world when the server saves that world")
@YamlKey("synchronization.save_on_world_save")
private boolean saveOnWorldSave = true; private boolean saveOnWorldSave = true;
@YamlComment("Whether to create a snapshot for users when they die (containing their death drops)") @Comment("Configuration for how and when to sync player data when they die")
@YamlKey("synchronization.save_on_death.enabled") private SaveOnDeathSettings saveOnDeath = new SaveOnDeathSettings();
private boolean saveOnDeath = false;
@YamlComment("What items to save in death snapshots? (DROPS or ITEMS_TO_KEEP). " @Getter
+ " Note that ITEMS_TO_KEEP (suggested for keepInventory servers) requires a Paper 1.19.4+ server.") @Configuration
@YamlKey("synchronization.save_on_death.items_to_save") @NoArgsConstructor(access = AccessLevel.PRIVATE)
private DeathItemsMode deathItemsMode = DeathItemsMode.DROPS; public static class SaveOnDeathSettings {
@Comment("Whether to create a snapshot for users when they die (containing their death drops)")
private boolean enabled = false;
@YamlComment("Should a death snapshot still be created even if the items to save on the player's death are empty?") @Comment("What items to save in death snapshots? (DROPS or ITEMS_TO_KEEP). "
@YamlKey("synchronization.save_on_death.save_empty_items") + "Note that ITEMS_TO_KEEP (suggested for keepInventory servers) requires a Paper 1.19.4+ server.")
private boolean saveEmptyDeathItems = true; private DeathItemsMode itemsToSave = DeathItemsMode.DROPS;
@YamlComment("Whether dead players who log out and log in to a different server should have their items saved.") @Comment("Should a death snapshot still be created even if the items to save on the player's death are empty?")
@YamlKey("synchronization.save_on_death.sync_dead_players_changing_server") private boolean saveEmptyItems = true;
private boolean synchronizeDeadPlayersChangingServer = true;
@YamlComment("Whether to use the snappy data compression algorithm. Keep on unless you know what you're doing") @Comment("Whether dead players who log out and log in to a different server should have their items saved.")
@YamlKey("synchronization.compress_data") private boolean syncDeadPlayersChangingServer = true;
private boolean compressData = true;
@YamlComment("Where to display sync notifications (ACTION_BAR, CHAT, TOAST or NONE)")
@YamlKey("synchronization.notification_display_slot")
private Locales.NotificationSlot notificationSlot = Locales.NotificationSlot.ACTION_BAR;
@YamlComment("(Experimental) Persist Cartography Table locked maps to let them be viewed on any server")
@YamlKey("synchronization.persist_locked_maps")
private boolean persistLockedMaps = true;
@YamlComment("Whether to synchronize player max health (requires health syncing to be enabled)")
@YamlKey("synchronization.synchronize_max_health")
private boolean synchronizeMaxHealth = true;
@YamlComment("If using the DELAY sync method, how long should this server listen for Redis key data updates before "
+ "pulling data from the database instead (i.e., if the user did not change servers).")
@YamlKey("synchronization.network_latency_milliseconds")
private int networkLatencyMilliseconds = 500;
@YamlComment("Which data types to synchronize (Docs: https://william278.net/docs/husksync/sync-features)")
@YamlKey("synchronization.features")
private Map<String, Boolean> synchronizationFeatures = Identifier.getConfigMap();
@YamlComment("Commands which should be blocked before a player has finished syncing (Use * to block all commands)")
@YamlKey("synchronization.blacklisted_commands_while_locked")
private List<String> blacklistedCommandsWhileLocked = new ArrayList<>(List.of("*"));
@YamlComment("Event priorities for listeners (HIGHEST, NORMAL, LOWEST). Change if you encounter plugin conflicts")
@YamlKey("synchronization.event_priorities")
private Map<String, String> syncEventPriorities = EventListener.ListenerType.getDefaults();
// Zero-args constructor for instantiation via Annotaml
@SuppressWarnings("unused")
public Settings() {
}
@NotNull
public String getLanguage() {
return language;
}
public boolean doCheckForUpdates() {
return checkForUpdates;
}
@NotNull
public String getClusterId() {
return clusterId;
}
public boolean doDebugLogging() {
return debugLogging;
}
public boolean doBrigadierTabCompletion() {
return brigadierTabCompletion;
}
public boolean usePlanHook() {
return enablePlanHook;
}
@NotNull
public Database.Type getDatabaseType() {
return databaseType;
}
@NotNull
public String getMySqlHost() {
return mySqlHost;
}
public int getMySqlPort() {
return mySqlPort;
}
@NotNull
public String getMySqlDatabase() {
return mySqlDatabase;
}
@NotNull
public String getMySqlUsername() {
return mySqlUsername;
}
@NotNull
public String getMySqlPassword() {
return mySqlPassword;
}
@NotNull
public String getMySqlConnectionParameters() {
return mySqlConnectionParameters;
}
@NotNull
public String getTableName(@NotNull TableName tableName) {
return tableNames.getOrDefault(tableName.name().toLowerCase(Locale.ENGLISH), tableName.defaultName);
}
public int getMySqlConnectionPoolSize() {
return mySqlConnectionPoolSize;
}
public int getMySqlConnectionPoolIdle() {
return mySqlConnectionPoolIdle;
}
public long getMySqlConnectionPoolLifetime() {
return mySqlConnectionPoolLifetime;
}
public long getMySqlConnectionPoolKeepAlive() {
return mySqlConnectionPoolKeepAlive;
}
public long getMySqlConnectionPoolTimeout() {
return mySqlConnectionPoolTimeout;
}
@NotNull
public String getRedisHost() {
return redisHost;
}
public int getRedisPort() {
return redisPort;
}
@NotNull
public String getRedisPassword() {
return redisPassword;
}
public boolean redisUseSsl() {
return redisUseSsl;
}
@NotNull
public String getRedisSentinelMaster() {
return redisSentinelMaster;
}
@NotNull
public List<String> getRedisSentinelNodes() {
return redisSentinelNodes;
}
@NotNull
public String getRedisSentinelPassword() {
return redisSentinelPassword;
}
@NotNull
public DataSyncer.Mode getSyncMode() {
return syncMode;
}
public int getMaxUserDataSnapshots() {
return maxUserDataSnapshots;
}
public int getBackupFrequency() {
return snapshotBackupFrequency;
}
public boolean doSaveOnWorldSave() {
return saveOnWorldSave;
}
public boolean doSaveOnDeath() {
return saveOnDeath;
}
@NotNull
public DeathItemsMode getDeathItemsMode() {
return deathItemsMode;
}
public boolean doSaveEmptyDeathItems() {
return saveEmptyDeathItems;
}
public boolean doCompressData() {
return compressData;
}
public boolean doAutoPin(@NotNull DataSnapshot.SaveCause cause) {
return autoPinnedSaveCauses.contains(cause.name());
}
@NotNull
public Locales.NotificationSlot getNotificationDisplaySlot() {
return notificationSlot;
}
public boolean doPersistLockedMaps() {
return persistLockedMaps;
}
public boolean doSynchronizeDeadPlayersChangingServer() {
return synchronizeDeadPlayersChangingServer;
}
public boolean doSynchronizeMaxHealth() {
return synchronizeMaxHealth;
}
public int getNetworkLatencyMilliseconds() {
return networkLatencyMilliseconds;
}
@NotNull
public Map<String, Boolean> getSynchronizationFeatures() {
return synchronizationFeatures;
}
public boolean isSyncFeatureEnabled(@NotNull Identifier id) {
return id.isCustom() || getSynchronizationFeatures().getOrDefault(id.getKeyValue(), id.isEnabledByDefault());
}
@NotNull
public List<String> getBlacklistedCommandsWhileLocked() {
return blacklistedCommandsWhileLocked;
}
@NotNull
public EventListener.Priority getEventPriority(@NotNull EventListener.ListenerType type) {
try {
return EventListener.Priority.valueOf(syncEventPriorities.get(type.name().toLowerCase(Locale.ENGLISH)));
} catch (IllegalArgumentException e) {
return EventListener.Priority.NORMAL;
}
}
/** /**
* Represents the mode of saving items on death * Represents the mode of saving items on death
@@ -439,31 +227,50 @@ public class Settings {
DROPS, DROPS,
ITEMS_TO_KEEP ITEMS_TO_KEEP
} }
}
/** @Comment("Whether to use the snappy data compression algorithm. Keep on unless you know what you're doing")
* Represents the names of tables in the database private boolean compressData = true;
*/
public enum TableName {
USERS("husksync_users"),
USER_DATA("husksync_user_data");
private final String defaultName; @Comment("Where to display sync notifications (ACTION_BAR, CHAT, TOAST or NONE)")
private Locales.NotificationSlot notificationDisplaySlot = Locales.NotificationSlot.ACTION_BAR;
TableName(@NotNull String defaultName) { @Comment("Persist maps locked in a Cartography Table to let them be viewed on any server")
this.defaultName = defaultName; private boolean persistLockedMaps = true;
@Comment("Whether to synchronize player max health (requires health syncing to be enabled)")
private boolean synchronizeMaxHealth = true;
@Comment("If using the DELAY sync method, how long should this server listen for Redis key data updates before "
+ "pulling data from the database instead (i.e., if the user did not change servers).")
private int networkLatencyMilliseconds = 500;
@Comment({"Which data types to synchronize.", "Docs: https://william278.net/docs/husksync/sync-features"})
@Getter(AccessLevel.NONE)
private Map<String, Boolean> features = Identifier.getConfigMap();
@Comment("Commands which should be blocked before a player has finished syncing (Use * to block all commands)")
private List<String> blacklistedCommandsWhileLocked = new ArrayList<>(List.of("*"));
@Comment("Event priorities for listeners (HIGHEST, NORMAL, LOWEST). Change if you encounter plugin conflicts")
@Getter(AccessLevel.NONE)
private Map<String, String> eventPriorities = EventListener.ListenerType.getDefaults();
public boolean doAutoPin(@NotNull DataSnapshot.SaveCause cause) {
return autoPinnedSaveCauses.contains(cause.name());
}
public boolean isFeatureEnabled(@NotNull Identifier id) {
return id.isCustom() || features.getOrDefault(id.getKeyValue(), id.isEnabledByDefault());
} }
@NotNull @NotNull
private Map.Entry<String, String> toEntry() { public EventListener.Priority getEventPriority(@NotNull EventListener.ListenerType type) {
return Map.entry(name().toLowerCase(Locale.ENGLISH), defaultName); try {
return EventListener.Priority.valueOf(eventPriorities.get(type.name().toLowerCase(Locale.ENGLISH)));
} catch (IllegalArgumentException e) {
return EventListener.Priority.NORMAL;
} }
@SuppressWarnings("unchecked")
@NotNull
private static Map<String, String> getDefaults() {
return Map.ofEntries(Arrays.stream(values())
.map(TableName::toEntry)
.toArray(Map.Entry[]::new));
} }
} }

View File

@@ -19,6 +19,7 @@
package net.william278.husksync.data; package net.william278.husksync.data;
import com.google.common.collect.Maps;
import com.google.gson.annotations.Expose; import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import de.themoep.minedown.adventure.MineDown; import de.themoep.minedown.adventure.MineDown;
@@ -26,7 +27,6 @@ import net.william278.desertwell.util.Version;
import net.william278.husksync.HuskSync; import net.william278.husksync.HuskSync;
import net.william278.husksync.adapter.Adaptable; import net.william278.husksync.adapter.Adaptable;
import net.william278.husksync.adapter.DataAdapter; import net.william278.husksync.adapter.DataAdapter;
import net.william278.husksync.config.Locales;
import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -304,7 +304,6 @@ public class DataSnapshot {
); );
} }
@NotNull
@ApiStatus.Internal @ApiStatus.Internal
public byte[] asBytes(@NotNull HuskSync plugin) throws DataAdapter.AdaptionException { public byte[] asBytes(@NotNull HuskSync plugin) throws DataAdapter.AdaptionException {
return plugin.getDataAdapter().toBytes(this); return plugin.getDataAdapter().toBytes(this);
@@ -427,7 +426,7 @@ public class DataSnapshot {
private Builder(@NotNull HuskSync plugin) { private Builder(@NotNull HuskSync plugin) {
this.plugin = plugin; this.plugin = plugin;
this.pinned = false; this.pinned = false;
this.data = new HashMap<>(); this.data = Maps.newHashMap();
this.timestamp = OffsetDateTime.now(); this.timestamp = OffsetDateTime.now();
this.id = UUID.randomUUID(); this.id = UUID.randomUUID();
this.serverName = plugin.getServerName(); this.serverName = plugin.getServerName();
@@ -718,7 +717,7 @@ public class DataSnapshot {
} }
return new Unpacked( return new Unpacked(
id, id,
pinned || plugin.getSettings().doAutoPin(saveCause), pinned || plugin.getSettings().getSynchronization().doAutoPin(saveCause),
timestamp, timestamp,
saveCause, saveCause,
serverName, serverName,
@@ -821,8 +820,7 @@ public class DataSnapshot {
@NotNull @NotNull
public String getDisplayName() { public String getDisplayName() {
return Locales.truncate(name().toLowerCase(Locale.ENGLISH) return name().toLowerCase(Locale.ENGLISH);
.replaceAll("_", " "), 18);
} }
@NotNull @NotNull

View File

@@ -43,7 +43,7 @@ public interface UserDataHolder extends DataHolder {
@NotNull @NotNull
default Map<Identifier, Data> getData() { default Map<Identifier, Data> getData() {
return getPlugin().getRegisteredDataTypes().stream() return getPlugin().getRegisteredDataTypes().stream()
.filter(type -> type.isCustom() || getPlugin().getSettings().isSyncFeatureEnabled(type)) .filter(type -> type.isCustom() || getPlugin().getSettings().getSynchronization().isFeatureEnabled(type))
.map(id -> Map.entry(id, getData(id))) .map(id -> Map.entry(id, getData(id)))
.filter(data -> data.getValue().isPresent()) .filter(data -> data.getValue().isPresent())
.collect(HashMap::new, (map, data) -> map.put(data.getKey(), data.getValue().get()), HashMap::putAll); .collect(HashMap::new, (map, data) -> map.put(data.getKey(), data.getValue().get()), HashMap::putAll);
@@ -105,7 +105,7 @@ public interface UserDataHolder extends DataHolder {
try { try {
for (Map.Entry<Identifier, Data> entry : unpacked.getData().entrySet()) { for (Map.Entry<Identifier, Data> entry : unpacked.getData().entrySet()) {
final Identifier identifier = entry.getKey(); final Identifier identifier = entry.getKey();
if (plugin.getSettings().isSyncFeatureEnabled(identifier)) { if (plugin.getSettings().getSynchronization().isFeatureEnabled(identifier)) {
if (identifier.isCustom()) { if (identifier.isCustom()) {
getCustomDataStore().put(identifier, entry.getValue()); getCustomDataStore().put(identifier, entry.getValue());
} }

View File

@@ -19,6 +19,7 @@
package net.william278.husksync.database; package net.william278.husksync.database;
import lombok.Getter;
import net.william278.husksync.HuskSync; import net.william278.husksync.HuskSync;
import net.william278.husksync.config.Settings; import net.william278.husksync.config.Settings;
import net.william278.husksync.data.DataSnapshot; import net.william278.husksync.data.DataSnapshot;
@@ -31,10 +32,7 @@ import org.jetbrains.annotations.NotNull;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
import java.util.List; import java.util.*;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
/** /**
* An abstract representation of the plugin database, storing player data. * An abstract representation of the plugin database, storing player data.
@@ -71,8 +69,9 @@ public abstract class Database {
*/ */
@NotNull @NotNull
protected final String formatStatementTables(@NotNull String sql) { protected final String formatStatementTables(@NotNull String sql) {
return sql.replaceAll("%users_table%", plugin.getSettings().getTableName(Settings.TableName.USERS)) final Settings.DatabaseSettings settings = plugin.getSettings().getDatabase();
.replaceAll("%user_data_table%", plugin.getSettings().getTableName(Settings.TableName.USER_DATA)); return sql.replaceAll("%users_table%", settings.getTableName(TableName.USERS))
.replaceAll("%user_data_table%", settings.getTableName(TableName.USER_DATA));
} }
/** /**
@@ -193,7 +192,7 @@ public abstract class Database {
*/ */
@Blocking @Blocking
private void addAndRotateSnapshot(@NotNull User user, @NotNull DataSnapshot.Packed snapshot) { private void addAndRotateSnapshot(@NotNull User user, @NotNull DataSnapshot.Packed snapshot) {
final int backupFrequency = plugin.getSettings().getBackupFrequency(); final int backupFrequency = plugin.getSettings().getSynchronization().getSnapshotBackupFrequency();
if (!snapshot.isPinned() && backupFrequency > 0) { if (!snapshot.isPinned() && backupFrequency > 0) {
this.rotateLatestSnapshot(user, snapshot.getTimestamp().minusHours(backupFrequency)); this.rotateLatestSnapshot(user, snapshot.getTimestamp().minusHours(backupFrequency));
} }
@@ -297,4 +296,31 @@ public abstract class Database {
} }
} }
/**
* Represents the names of tables in the database
*/
@Getter
public enum TableName {
USERS("husksync_users"),
USER_DATA("husksync_user_data");
private final String defaultName;
TableName(@NotNull String defaultName) {
this.defaultName = defaultName;
}
@NotNull
private Map.Entry<String, String> toEntry() {
return Map.entry(name().toLowerCase(Locale.ENGLISH), defaultName);
}
@SuppressWarnings("unchecked")
@NotNull
public static Map<String, String> getDefaults() {
return Map.ofEntries(Arrays.stream(values())
.map(TableName::toEntry)
.toArray(Map.Entry[]::new));
}
}
} }

View File

@@ -19,6 +19,7 @@
package net.william278.husksync.database; package net.william278.husksync.database;
import com.google.common.collect.Lists;
import com.zaxxer.hikari.HikariDataSource; import com.zaxxer.hikari.HikariDataSource;
import net.william278.husksync.HuskSync; import net.william278.husksync.HuskSync;
import net.william278.husksync.adapter.DataAdapter; import net.william278.husksync.adapter.DataAdapter;
@@ -34,6 +35,8 @@ import java.time.OffsetDateTime;
import java.util.*; import java.util.*;
import java.util.logging.Level; import java.util.logging.Level;
import static net.william278.husksync.config.Settings.DatabaseSettings;
public class MySqlDatabase extends Database { public class MySqlDatabase extends Database {
private static final String DATA_POOL_NAME = "HuskSyncHikariPool"; private static final String DATA_POOL_NAME = "HuskSyncHikariPool";
@@ -43,9 +46,10 @@ public class MySqlDatabase extends Database {
public MySqlDatabase(@NotNull HuskSync plugin) { public MySqlDatabase(@NotNull HuskSync plugin) {
super(plugin); super(plugin);
this.flavor = plugin.getSettings().getDatabaseType().getProtocol();
this.driverClass = plugin.getSettings().getDatabaseType() == Type.MARIADB final Type type = plugin.getSettings().getDatabase().getType();
? "org.mariadb.jdbc.Driver" : "com.mysql.cj.jdbc.Driver"; this.flavor = type.getProtocol();
this.driverClass = type == Type.MARIADB ? "org.mariadb.jdbc.Driver" : "com.mysql.cj.jdbc.Driver";
} }
/** /**
@@ -67,26 +71,28 @@ public class MySqlDatabase extends Database {
@Override @Override
public void initialize() throws IllegalStateException { public void initialize() throws IllegalStateException {
// Initialize the Hikari pooled connection // Initialize the Hikari pooled connection
final DatabaseSettings.DatabaseCredentials credentials = plugin.getSettings().getDatabase().getCredentials();
dataSource = new HikariDataSource(); dataSource = new HikariDataSource();
dataSource.setDriverClassName(driverClass); dataSource.setDriverClassName(driverClass);
dataSource.setJdbcUrl(String.format("jdbc:%s://%s:%s/%s%s", dataSource.setJdbcUrl(String.format("jdbc:%s://%s:%s/%s%s",
flavor, flavor,
plugin.getSettings().getMySqlHost(), credentials.getHost(),
plugin.getSettings().getMySqlPort(), credentials.getPort(),
plugin.getSettings().getMySqlDatabase(), credentials.getDatabase(),
plugin.getSettings().getMySqlConnectionParameters() credentials.getParameters()
)); ));
// Authenticate with the database // Authenticate with the database
dataSource.setUsername(plugin.getSettings().getMySqlUsername()); dataSource.setUsername(credentials.getUsername());
dataSource.setPassword(plugin.getSettings().getMySqlPassword()); dataSource.setPassword(credentials.getPassword());
// Set connection pool options // Set connection pool options
dataSource.setMaximumPoolSize(plugin.getSettings().getMySqlConnectionPoolSize()); final DatabaseSettings.PoolSettings pool = plugin.getSettings().getDatabase().getConnectionPool();
dataSource.setMinimumIdle(plugin.getSettings().getMySqlConnectionPoolIdle()); dataSource.setMaximumPoolSize(pool.getMaximumPoolSize());
dataSource.setMaxLifetime(plugin.getSettings().getMySqlConnectionPoolLifetime()); dataSource.setMinimumIdle(pool.getMinimumIdle());
dataSource.setKeepaliveTime(plugin.getSettings().getMySqlConnectionPoolKeepAlive()); dataSource.setMaxLifetime(pool.getMaximumLifetime());
dataSource.setConnectionTimeout(plugin.getSettings().getMySqlConnectionPoolTimeout()); dataSource.setKeepaliveTime(pool.getKeepaliveTime());
dataSource.setConnectionTimeout(pool.getConnectionTimeout());
dataSource.setPoolName(DATA_POOL_NAME); dataSource.setPoolName(DATA_POOL_NAME);
// Set additional connection pool properties // Set additional connection pool properties
@@ -245,7 +251,7 @@ public class MySqlDatabase extends Database {
@Override @Override
@NotNull @NotNull
public List<DataSnapshot.Packed> getAllSnapshots(@NotNull User user) { public List<DataSnapshot.Packed> getAllSnapshots(@NotNull User user) {
final List<DataSnapshot.Packed> retrievedData = new ArrayList<>(); final List<DataSnapshot.Packed> retrievedData = Lists.newArrayList();
try (Connection connection = getConnection()) { try (Connection connection = getConnection()) {
try (PreparedStatement statement = connection.prepareStatement(formatStatementTables(""" try (PreparedStatement statement = connection.prepareStatement(formatStatementTables("""
SELECT `version_uuid`, `timestamp`, `data` SELECT `version_uuid`, `timestamp`, `data`
@@ -306,7 +312,8 @@ public class MySqlDatabase extends Database {
protected void rotateSnapshots(@NotNull User user) { protected void rotateSnapshots(@NotNull User user) {
final List<DataSnapshot.Packed> unpinnedUserData = getAllSnapshots(user).stream() final List<DataSnapshot.Packed> unpinnedUserData = getAllSnapshots(user).stream()
.filter(dataSnapshot -> !dataSnapshot.isPinned()).toList(); .filter(dataSnapshot -> !dataSnapshot.isPinned()).toList();
if (unpinnedUserData.size() > plugin.getSettings().getMaxUserDataSnapshots()) { final int maxSnapshots = plugin.getSettings().getSynchronization().getMaxUserDataSnapshots();
if (unpinnedUserData.size() > maxSnapshots) {
try (Connection connection = getConnection()) { try (Connection connection = getConnection()) {
try (PreparedStatement statement = connection.prepareStatement(formatStatementTables(""" try (PreparedStatement statement = connection.prepareStatement(formatStatementTables("""
DELETE FROM `%user_data_table%` DELETE FROM `%user_data_table%`
@@ -314,7 +321,7 @@ public class MySqlDatabase extends Database {
AND `pinned` IS FALSE AND `pinned` IS FALSE
ORDER BY `timestamp` ASC ORDER BY `timestamp` ASC
LIMIT %entry_count%;""".replace("%entry_count%", LIMIT %entry_count%;""".replace("%entry_count%",
Integer.toString(unpinnedUserData.size() - plugin.getSettings().getMaxUserDataSnapshots()))))) { Integer.toString(unpinnedUserData.size() - maxSnapshots))))) {
statement.setString(1, user.getUuid().toString()); statement.setString(1, user.getUuid().toString());
statement.executeUpdate(); statement.executeUpdate();
} }

View File

@@ -30,6 +30,8 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import static net.william278.husksync.config.Settings.SynchronizationSettings.SaveOnDeathSettings;
/** /**
* Handles what should happen when events are fired * Handles what should happen when events are fired
*/ */
@@ -74,7 +76,7 @@ public abstract class EventListener {
* @param usersInWorld a list of users in the world that is being saved * @param usersInWorld a list of users in the world that is being saved
*/ */
protected final void saveOnWorldSave(@NotNull List<OnlineUser> usersInWorld) { protected final void saveOnWorldSave(@NotNull List<OnlineUser> usersInWorld) {
if (plugin.isDisabling() || !plugin.getSettings().doSaveOnWorldSave()) { if (plugin.isDisabling() || !plugin.getSettings().getSynchronization().isSaveOnWorldSave()) {
return; return;
} }
usersInWorld.stream() usersInWorld.stream()
@@ -91,8 +93,9 @@ public abstract class EventListener {
* @param items The items that should be saved for this user on their death * @param items The items that should be saved for this user on their death
*/ */
protected void saveOnPlayerDeath(@NotNull OnlineUser user, @NotNull Data.Items items) { protected void saveOnPlayerDeath(@NotNull OnlineUser user, @NotNull Data.Items items) {
if (plugin.isDisabling() || !plugin.getSettings().doSaveOnDeath() || plugin.isLocked(user.getUuid()) final SaveOnDeathSettings settings = plugin.getSettings().getSynchronization().getSaveOnDeath();
|| user.isNpc() || (!plugin.getSettings().doSaveEmptyDeathItems() && items.isEmpty())) { if (plugin.isDisabling() || !settings.isEnabled() || plugin.isLocked(user.getUuid())
|| user.isNpc() || (!settings.isSaveEmptyItems() && items.isEmpty())) {
return; return;
} }

View File

@@ -20,6 +20,7 @@
package net.william278.husksync.redis; package net.william278.husksync.redis;
import net.william278.husksync.HuskSync; import net.william278.husksync.HuskSync;
import net.william278.husksync.config.Settings;
import net.william278.husksync.data.DataSnapshot; import net.william278.husksync.data.DataSnapshot;
import net.william278.husksync.user.User; import net.william278.husksync.user.User;
import org.jetbrains.annotations.Blocking; import org.jetbrains.annotations.Blocking;
@@ -41,12 +42,16 @@ import java.util.logging.Level;
public class RedisManager extends JedisPubSub { public class RedisManager extends JedisPubSub {
protected static final String KEY_NAMESPACE = "husksync:"; protected static final String KEY_NAMESPACE = "husksync:";
private static final int RECONNECTION_TIME = 8000;
private final HuskSync plugin; private final HuskSync plugin;
private final String clusterId; private final String clusterId;
private Pool<Jedis> jedisPool; private Pool<Jedis> jedisPool;
private final Map<UUID, CompletableFuture<Optional<DataSnapshot.Packed>>> pendingRequests; private final Map<UUID, CompletableFuture<Optional<DataSnapshot.Packed>>> pendingRequests;
private boolean enabled;
private boolean reconnected;
public RedisManager(@NotNull HuskSync plugin) { public RedisManager(@NotNull HuskSync plugin) {
this.plugin = plugin; this.plugin = plugin;
this.clusterId = plugin.getSettings().getClusterId(); this.clusterId = plugin.getSettings().getClusterId();
@@ -58,25 +63,28 @@ public class RedisManager extends JedisPubSub {
*/ */
@Blocking @Blocking
public void initialize() throws IllegalStateException { public void initialize() throws IllegalStateException {
final String password = plugin.getSettings().getRedisPassword(); final Settings.RedisSettings.RedisCredentials credentials = plugin.getSettings().getRedis().getCredentials();
final String host = plugin.getSettings().getRedisHost(); final String password = credentials.getPassword();
final int port = plugin.getSettings().getRedisPort(); final String host = credentials.getHost();
final boolean useSSL = plugin.getSettings().redisUseSsl(); final int port = credentials.getPort();
final boolean useSSL = credentials.isUseSsl();
// Create the jedis pool // Create the jedis pool
final JedisPoolConfig config = new JedisPoolConfig(); final JedisPoolConfig config = new JedisPoolConfig();
config.setMaxIdle(0); config.setMaxIdle(0);
config.setTestOnBorrow(true); config.setTestOnBorrow(true);
config.setTestOnReturn(true); config.setTestOnReturn(true);
Set<String> redisSentinelNodes = new HashSet<>(plugin.getSettings().getRedisSentinelNodes());
final Settings.RedisSettings.RedisSentinel sentinel = plugin.getSettings().getRedis().getSentinel();
Set<String> redisSentinelNodes = new HashSet<>(sentinel.getNodes());
if (redisSentinelNodes.isEmpty()) { if (redisSentinelNodes.isEmpty()) {
this.jedisPool = password.isEmpty() this.jedisPool = password.isEmpty()
? new JedisPool(config, host, port, 0, useSSL) ? new JedisPool(config, host, port, 0, useSSL)
: new JedisPool(config, host, port, 0, password, useSSL); : new JedisPool(config, host, port, 0, password, useSSL);
} else { } else {
String sentinelPassword = plugin.getSettings().getRedisSentinelPassword(); final String sentinelPassword = sentinel.getPassword();
String redisSentinelMaster = plugin.getSettings().getRedisSentinelMaster(); this.jedisPool = new JedisSentinelPool(sentinel.getMaster(), redisSentinelNodes, password.isEmpty()
this.jedisPool = new JedisSentinelPool(redisSentinelMaster, redisSentinelNodes, password.isEmpty() ? null : password, sentinelPassword.isEmpty() ? null : sentinelPassword); ? null : password, sentinelPassword.isEmpty() ? null : sentinelPassword);
} }
// Ping the server to check the connection // Ping the server to check the connection
@@ -88,18 +96,55 @@ public class RedisManager extends JedisPubSub {
} }
// Subscribe using a thread (rather than a task) // Subscribe using a thread (rather than a task)
enabled = true;
new Thread(this::subscribe, "husksync:redis_subscriber").start(); new Thread(this::subscribe, "husksync:redis_subscriber").start();
} }
@Blocking @Blocking
private void subscribe() { private void subscribe() {
while (enabled && !Thread.interrupted() && jedisPool != null && !jedisPool.isClosed()) {
try (Jedis jedis = jedisPool.getResource()) { try (Jedis jedis = jedisPool.getResource()) {
if (reconnected) {
plugin.log(Level.INFO, "Redis connection is alive again");
}
// Subscribe channels and lock the thread
jedis.subscribe( jedis.subscribe(
this, this,
Arrays.stream(RedisMessage.Type.values()) Arrays.stream(RedisMessage.Type.values())
.map(type -> type.getMessageChannel(clusterId)) .map(type -> type.getMessageChannel(clusterId))
.toArray(String[]::new) .toArray(String[]::new)
); );
} catch (Throwable t) {
// Thread was unlocked due error
onThreadUnlock(t);
}
}
}
private void onThreadUnlock(@NotNull Throwable t) {
if (!enabled) {
return;
}
if (reconnected) {
plugin.log(Level.WARNING, "Redis Server connection lost. Attempting reconnect in %ss..."
.formatted(RECONNECTION_TIME / 1000), t);
}
try {
this.unsubscribe();
} catch (Throwable ignored) {
// empty catch
}
// Make an instant subscribe if occurs any error on initialization
if (!reconnected) {
reconnected = true;
} else {
try {
Thread.sleep(RECONNECTION_TIME);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
} }
} }
@@ -136,6 +181,16 @@ public class RedisManager extends JedisPubSub {
} }
} }
@Override
public void onSubscribe(String channel, int subscribedChannels) {
plugin.log(Level.INFO, "Redis subscribed to channel '" + channel + "'");
}
@Override
public void onUnsubscribe(String channel, int subscribedChannels) {
plugin.log(Level.INFO, "Redis unsubscribed from channel '" + channel + "'");
}
@Blocking @Blocking
protected void sendMessage(@NotNull String channel, @NotNull String message) { protected void sendMessage(@NotNull String channel, @NotNull String message) {
try (Jedis jedis = jedisPool.getResource()) { try (Jedis jedis = jedisPool.getResource()) {
@@ -168,8 +223,9 @@ public class RedisManager extends JedisPubSub {
); );
redisMessage.dispatch(plugin, RedisMessage.Type.REQUEST_USER_DATA); redisMessage.dispatch(plugin, RedisMessage.Type.REQUEST_USER_DATA);
}); });
return future.orTimeout( return future
plugin.getSettings().getNetworkLatencyMilliseconds(), .orTimeout(
plugin.getSettings().getSynchronization().getNetworkLatencyMilliseconds(),
TimeUnit.MILLISECONDS TimeUnit.MILLISECONDS
) )
.exceptionally(throwable -> { .exceptionally(throwable -> {
@@ -329,6 +385,7 @@ public class RedisManager extends JedisPubSub {
@Blocking @Blocking
public void terminate() { public void terminate() {
enabled = false;
if (jedisPool != null) { if (jedisPool != null) {
if (!jedisPool.isClosed()) { if (!jedisPool.isClosed()) {
jedisPool.close(); jedisPool.close();

View File

@@ -27,6 +27,7 @@ import net.william278.husksync.util.Task;
import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function; import java.util.function.Function;
@@ -89,7 +90,8 @@ public abstract class DataSyncer {
// Calculates the max attempts the system should listen for user data for based on the latency value // Calculates the max attempts the system should listen for user data for based on the latency value
private long getMaxListenAttempts() { private long getMaxListenAttempts() {
return BASE_LISTEN_ATTEMPTS + ( return BASE_LISTEN_ATTEMPTS + (
(Math.max(100, plugin.getSettings().getNetworkLatencyMilliseconds()) / 1000) * 20 / LISTEN_DELAY (Math.max(100, plugin.getSettings().getSynchronization().getNetworkLatencyMilliseconds()) / 1000)
* 20 / LISTEN_DELAY
); );
} }
@@ -107,11 +109,18 @@ public abstract class DataSyncer {
protected void listenForRedisData(@NotNull OnlineUser user, @NotNull Supplier<Boolean> completionSupplier) { protected void listenForRedisData(@NotNull OnlineUser user, @NotNull Supplier<Boolean> completionSupplier) {
final AtomicLong timesRun = new AtomicLong(0L); final AtomicLong timesRun = new AtomicLong(0L);
final AtomicReference<Task.Repeating> task = new AtomicReference<>(); final AtomicReference<Task.Repeating> task = new AtomicReference<>();
final AtomicBoolean processing = new AtomicBoolean(false);
final Runnable runnable = () -> { final Runnable runnable = () -> {
if (user.isOffline()) { if (user.isOffline()) {
task.get().cancel(); task.get().cancel();
return; return;
} }
// Ensure only one task is running at a time
if (processing.getAndSet(true)) {
return;
}
// Timeout if the plugin is disabling or the max attempts have been reached
if (plugin.isDisabling() || timesRun.getAndIncrement() > maxListenAttempts) { if (plugin.isDisabling() || timesRun.getAndIncrement() > maxListenAttempts) {
task.get().cancel(); task.get().cancel();
plugin.debug(String.format("[%s] Redis timed out after %s attempts; setting from database", plugin.debug(String.format("[%s] Redis timed out after %s attempts; setting from database",
@@ -120,9 +129,11 @@ public abstract class DataSyncer {
return; return;
} }
// Fire the completion supplier
if (completionSupplier.get()) { if (completionSupplier.get()) {
task.get().cancel(); task.get().cancel();
} }
processing.set(false);
}; };
task.set(plugin.getRepeatingTask(runnable, LISTEN_DELAY)); task.set(plugin.getRepeatingTask(runnable, LISTEN_DELAY));
task.get().run(); task.get().run();

View File

@@ -53,7 +53,7 @@ public class DelayDataSyncer extends DataSyncer {
}).orElse(false) }).orElse(false)
); );
}, },
Math.max(0, plugin.getSettings().getNetworkLatencyMilliseconds() / 50L) Math.max(0, plugin.getSettings().getSynchronization().getNetworkLatencyMilliseconds() / 50L)
); );
} }

View File

@@ -147,7 +147,7 @@ public abstract class OnlineUser extends User implements CommandUser, UserDataHo
*/ */
public void completeSync(boolean succeeded, @NotNull DataSnapshot.UpdateCause cause, @NotNull HuskSync plugin) { public void completeSync(boolean succeeded, @NotNull DataSnapshot.UpdateCause cause, @NotNull HuskSync plugin) {
if (succeeded) { if (succeeded) {
switch (plugin.getSettings().getNotificationDisplaySlot()) { switch (plugin.getSettings().getSynchronization().getNotificationDisplaySlot()) {
case CHAT -> cause.getCompletedLocale(plugin).ifPresent(this::sendMessage); case CHAT -> cause.getCompletedLocale(plugin).ifPresent(this::sendMessage);
case ACTION_BAR -> cause.getCompletedLocale(plugin).ifPresent(this::sendActionBar); case ACTION_BAR -> cause.getCompletedLocale(plugin).ifPresent(this::sendActionBar);
case TOAST -> cause.getCompletedLocale(plugin) case TOAST -> cause.getCompletedLocale(plugin)

View File

@@ -1,62 +1,63 @@
synchronization_complete: '[⏵ Данните синхронизирани!](#00fb9a)' locales:
synchronization_failed: '[⏵ Провалихме се да синхронизираме Вашите данни! Моля свържете се с администратор.](#ff7e5e)' synchronization_complete: '[⏵ Данните синхронизирани!](#00fb9a)'
inventory_viewer_menu_title: '&0Инвентара на %1%' synchronization_failed: '[⏵ Провалихме се да синхронизираме Вашите данни! Моля свържете се с администратор.](#ff7e5e)'
ender_chest_viewer_menu_title: '&0Ендър Сандъка на %1%' inventory_viewer_menu_title: '&0Инвентара на %1%'
inventory_viewer_opened: '[Преглеждане снапшота на](#00fb9a) [%1%](#00fb9a bold) [ инвентар от ⌚ %2%](#00fb9a)' ender_chest_viewer_menu_title: '&0Ендър Сандъка на %1%'
ender_chest_viewer_opened: '[Преглеждане снапшота на](#00fb9a) [%1%](#00fb9a bold) [ Ендър Сандък от ⌚ %2%](#00fb9a)' inventory_viewer_opened: '[Преглеждане снапшота на](#00fb9a) [%1%](#00fb9a bold) [ инвентар от ⌚ %2%](#00fb9a)'
data_update_complete: '[🔔 Вашите данни бяха обновени!](#00fb9a)' ender_chest_viewer_opened: '[Преглеждане снапшота на](#00fb9a) [%1%](#00fb9a bold) [ Ендър Сандък от ⌚ %2%](#00fb9a)'
data_update_failed: '[🔔 Провалихме се да обновим Вашите данни! Моля свържете се с администратор.](#ff7e5e)' data_update_complete: '[🔔 Вашите данни бяха обновени!](#00fb9a)'
user_registration_complete: '[⭐ User registration complete!](#00fb9a)' data_update_failed: '[🔔 Провалихме се да обновим Вашите данни! Моля свържете се с администратор.](#ff7e5e)'
data_manager_title: '[Преглеждане потребителският снапшот](#00fb9a) [%1%](#00fb9a show_text=&7Версия на UUID:\n&8%2%) [за](#00fb9a) [%3%](#00fb9a bold show_text=&7UUID на Играча:\n&8%4%)[:](#00fb9a)' user_registration_complete: '[⭐ User registration complete!](#00fb9a)'
data_manager_timestamp: '[⌚ %1%](#ffc43b-#f5c962 show_text=&7Клеймо на Версията:\n&8Когато данните са били запазени)' data_manager_title: '[Преглеждане потребителският снапшот](#00fb9a) [%1%](#00fb9a show_text=&7Версия на UUID:\n&8%2%) [за](#00fb9a) [%3%](#00fb9a bold show_text=&7UUID на Играча:\n&8%4%)[:](#00fb9a)'
data_manager_pinned: '[※ Закачен снапшот](#d8ff2b show_text=&7Закачен:\n&8Снапшота на този потребител няма да бъде автоматично завъртан.)' data_manager_timestamp: '[⌚ %1%](#ffc43b-#f5c962 show_text=&7Клеймо на Версията:\n&8Когато данните са били запазени)'
data_manager_cause: '[⚑ %1%](#23a825-#36f539 show_text=&7Причина на Запазване:\n&8Какво е накарало данните да бъдат запазени)' data_manager_pinned: '[※ Закачен снапшот](#d8ff2b show_text=&7Закачен:\n&8Снапшота на този потребител няма да бъде автоматично завъртан.)'
data_manager_server: '[ %1%](#ff87b3-#f5538e show_text=&7Server:\n&8Name of the server the data was saved on)' data_manager_cause: '[ %1%](#23a825-#36f539 show_text=&7Причина на Запазване:\n&8Какво е накарало данните да бъдат запазени)'
data_manager_size: '[ %1%](color=#62a9f5-#7ab8fa show_text=&7Snapshot size:\n&8Estimated file size of the snapshot (in KiB))\n' data_manager_server: '[ %1%](#ff87b3-#f5538e show_text=&7Server:\n&8Name of the server the data was saved on)'
data_manger_status: '[%1%](red)[/](gray)[%2%](red)[×](gray)[❤](red show_text=&7Точки кръв) [%3%](yellow)[×](gray)[🍖](yellow show_text=&7Точки глад) [ʟᴠ](green)[.](gray)[%4%](green show_text=&7Ниво опит) [🏹 %5%](dark_aqua show_text=&7Режим на игра)' data_manager_size: '[%1%](color=#62a9f5-#7ab8fa show_text=&7Snapshot size:\n&8Estimated file size of the snapshot (in KiB))\n'
data_manager_advancements_statistics: '[⭐ Напредъци: %1%](color=#ffc43b-#f5c962 show_text=&7Напредъци, в които имате прогрес:\n&8%2%) [⌛ Изиграно Време: %3%ʜʀs](color=#62a9f5-#7ab8fa show_text=&7Изиграно време в играта\n&8⚠ Базирано на статистики от играта)\n' data_manger_status: '[%1%](red)[/](gray)[%2%](red)[×](gray)[❤](red show_text=&7Точки кръв) [%3%](yellow)[×](gray)[🍖](yellow show_text=&7Точки глад) [ʟᴠ](green)[.](gray)[%4%](green show_text=&7Ниво опит) [🏹 %5%](dark_aqua show_text=&7Режим на игра)'
data_manager_item_buttons: '[View:](gray) [[🪣 Инвентар…]](color=#a17b5f-#f5b98c show_text=&7Натиснете, за да прегледате run_command=/inventory %1% %2%) [[⌀ Ендър Сандък…]](#b649c4-#d254ff show_text=&7Натиснете, за да проверите run_command=/enderchest %1% %2%)' data_manager_advancements_statistics: '[⭐ Напредъци: %1%](color=#ffc43b-#f5c962 show_text=&7Напредъци, в които имате прогрес:\n&8%2%) [⌛ Изиграно Време: %3%ʜʀs](color=#62a9f5-#7ab8fa show_text=&7Изиграно време в играта\n&8⚠ Базирано на статистики от играта)\n'
data_manager_management_buttons: '[Управление:](gray) [[ Изтрий…]](#ff3300 show_text=&7Натиснете, за да изтриете този снапшот от потребителски данни.\n&8Това няма да засегне текущите данни на потребителя.\n&#ff3300&⚠ Това не може да бъде отменено! suggest_command=/husksync:userdata delete %1% %2%) [[⏪ Възстанови…]](#00fb9a show_text=&7Натиснете, за да възстановите тези потребителски данни.\n&8Това ще зададе данните на потребителя на този снапшот.\n&#ff3300&⚠ Текущите данни на %1% ще бъдат пренаписани! suggest_command=/husksync:userdata restore %1% %2%) [[※ Закачи/Откачи…]](#d8ff2b show_text=&7Натиснете, за да закачите или откачите този снапшот с потребителски данни\n&8Закачените снапшоти няма да бъдат автоматично завъртани run_command=/userdata pin %1% %2%)' data_manager_item_buttons: '[View:](gray) [[🪣 Инвентар…]](color=#a17b5f-#f5b98c show_text=&7Натиснете, за да прегледате run_command=/inventory %1% %2%) [[⌀ Ендър Сандък…]](#b649c4-#d254ff show_text=&7Натиснете, за да проверите run_command=/enderchest %1% %2%)'
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_management_buttons: '[Управление:](gray) [[❌ Изтрий…]](#ff3300 show_text=&7Натиснете, за да изтриете този снапшот от потребителски данни.\n&8Това няма да засегне текущите данни на потребителя.\n&#ff3300&⚠ Това не може да бъде отменено! suggest_command=/husksync:userdata delete %1% %2%) [[⏪ Възстанови…]](#00fb9a show_text=&7Натиснете, за да възстановите тези потребителски данни.\n&8Това ще зададе данните на потребителя на този снапшот.\n&#ff3300&⚠ Текущите данни на %1% ще бъдат пренаписани! suggest_command=/husksync:userdata restore %1% %2%) [[※ Закачи/Откачи…]](#d8ff2b show_text=&7Натиснете, за да закачите или откачите този снапшот с потребителски данни\n&8Закачените снапшоти няма да бъдат автоматично завъртани run_command=/userdata pin %1% %2%)'
data_manager_advancements_preview_remaining: 'и още %1%…' 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_list_title: '[Лист от](#00fb9a) [снапшоти на данните на потребителя](#00fb9a) [%1%](#00fb9a bold show_text=&7UUID: %2%)\n' data_manager_advancements_preview_remaining: 'и още %1%…'
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_title: '[Лист от](#00fb9a) [снапшоти на данните на потребителя](#00fb9a) [%1%](#00fb9a bold show_text=&7UUID: %2%)\n'
data_deleted: '[❌ Успешно изтрихме снапшота с потребителски данни](#00fb9a) [%1%](#00fb9a show_text=&7Версия на UUID:\n&8%2%) [за](#00fb9a) [%3%.](#00fb9a show_text=&7UUID на Играча:\n&8%4%)' 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_restored: '[ Успешно възстановихме](#00fb9a) [текущите потребителски данни за](#00fb9a) [%1%](#00fb9a show_text=&7UUID на Играча:\n&8%2%) [от снапшот](#00fb9a) [%3%.](#00fb9a show_text=&7Версия на UUID:\n&8%4%)' data_deleted: '[ Успешно изтрихме снапшота с потребителски данни](#00fb9a) [%1%](#00fb9a show_text=&7Версия на UUID:\n&8%2%) [за](#00fb9a) [%3%.](#00fb9a show_text=&7UUID на Играча:\n&8%4%)'
data_pinned: '[ Успешно закачихме снапшота с потребителски данни](#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_unpinned: '[※ Успешно откачихме снапшота с потребителски данни](#00fb9a) [%1%](#00fb9a show_text=&7Версия на UUID:\n&8%2%) [за](#00fb9a) [%3%.](#00fb9a show_text=&7UUID на Играча:\n&8%4%)' data_pinned: '[※ Успешно закачихме снапшота с потребителски данни](#00fb9a) [%1%](#00fb9a show_text=&7Версия на UUID:\n&8%2%) [за](#00fb9a) [%3%.](#00fb9a show_text=&7UUID на Играча:\n&8%4%)'
data_dumped: '[☂ Successfully dumped the user data snapshot %1% for %2% to:](#00fb9a) &7%3%' data_unpinned: '[※ Успешно откачихме снапшота с потребителски данни](#00fb9a) [%1%](#00fb9a show_text=&7Версия на UUID:\n&8%2%) [за](#00fb9a) [%3%.](#00fb9a show_text=&7UUID на Играча:\n&8%4%)'
list_footer: '\n%1%[Page](#00fb9a) [%2%](#00fb9a)/[%3%](#00fb9a)%4% %5%' data_dumped: '[☂ Successfully dumped the user data snapshot %1% for %2% to:](#00fb9a) &7%3%'
list_previous_page_button: '[◀](white show_text=&7View previous page run_command=%2% %1%) ' list_footer: '\n%1%[Page](#00fb9a) [%2%](#00fb9a)/[%3%](#00fb9a)%4% %5%'
list_next_page_button: ' [▶](white show_text=&7View next page run_command=%2% %1%)' list_previous_page_button: '[◀](white show_text=&7View previous page run_command=%2% %1%) '
list_page_jumpers: '(%1%)' list_next_page_button: ' [▶](white show_text=&7View next page run_command=%2% %1%)'
list_page_jumper_button: '[%1%](show_text=&7Jump to page %1% run_command=%2% %1%)' list_page_jumpers: '(%1%)'
list_page_jumper_current_page: '[%1%](#00fb9a)' list_page_jumper_button: '[%1%](show_text=&7Jump to page %1% run_command=%2% %1%)'
list_page_jumper_separator: ' ' list_page_jumper_current_page: '[%1%](#00fb9a)'
list_page_jumper_group_separator: '' list_page_jumper_separator: ' '
save_cause_disconnect: 'disconnect' list_page_jumper_group_separator: '…'
save_cause_world_save: 'world save' save_cause_disconnect: 'disconnect'
save_cause_death: 'death' save_cause_world_save: 'world save'
save_cause_server_shutdown: 'server shutdown' save_cause_death: 'death'
save_cause_inventory_command: 'inventory command' save_cause_server_shutdown: 'server shutdown'
save_cause_enderchest_command: 'enderchest command' save_cause_inventory_command: 'inventory command'
save_cause_backup_restore: 'backup restore' save_cause_enderchest_command: 'enderchest command'
save_cause_api: 'API' save_cause_backup_restore: 'backup restore'
save_cause_mpdb_migration: 'MPDB migration' save_cause_api: 'API'
save_cause_legacy_migration: 'legacy migration' save_cause_mpdb_migration: 'MPDB migration'
save_cause_converted_from_v2: 'converted from v2' save_cause_legacy_migration: 'legacy migration'
up_to_date: '[HuskSync](#00fb9a bold) [| You are running the latest version of HuskSync (v%1%).](#00fb9a)' save_cause_converted_from_v2: 'converted from v2'
update_available: '[HuskSync](#ff7e5e bold) [| A new version of HuskSync is available: v%1% (running: v%2%).](#ff7e5e)' up_to_date: '[HuskSync](#00fb9a bold) [| You are running the latest version of HuskSync (v%1%).](#00fb9a)'
reload_complete: '[HuskSync](#00fb9a bold) [| Презаредихме конфигурацията и файловете със съобщения.](#00fb9a)\n[⚠ Ensure config files are up-to-date on all servers!](#00fb9a)\n[A restart is needed for config changes to take effect.](#00fb9a italic)' update_available: '[HuskSync](#ff7e5e bold) [| A new version of HuskSync is available: v%1% (running: v%2%).](#ff7e5e)'
system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)' reload_complete: '[HuskSync](#00fb9a bold) [| Презаредихме конфигурацията и файловете със съобщения.](#00fb9a)\n[⚠ Ensure config files are up-to-date on all servers!](#00fb9a)\n[A restart is needed for config changes to take effect.](#00fb9a italic)'
error_invalid_syntax: '[Грешка:](#ff3300) [Неправилен синтаксис. Използвайте:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)' system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)'
error_invalid_player: '[Грешка:](#ff3300) [Не можахме да открием играч с това име.](#ff7e5e)' error_invalid_syntax: '[Грешка:](#ff3300) [Неправилен синтаксис. Използвайте:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)'
error_no_permission: '[Грешка:](#ff3300) [Нямате право да използвате тази команда](#ff7e5e)' error_invalid_player: '[Грешка:](#ff3300) [Не можахме да открием играч с това име.](#ff7e5e)'
error_console_command_only: '[Грешка:](#ff3300) [Тази команда може да бъде използвана единствено през конзолата](#ff7e5e)' error_no_permission: '[Грешка:](#ff3300) [Нямате право да използвате тази команда](#ff7e5e)'
error_in_game_command_only: 'Грешка: Тази команда може да бъде използвана само от играта.' error_console_command_only: '[Грешка:](#ff3300) [Тази команда може да бъде използвана единствено през конзолата](#ff7e5e)'
error_no_data_to_display: '[Грешка:](#ff3300) [Не можахме да открием никакви данни за потребителя, които да покажем.](#ff7e5e)' error_in_game_command_only: 'Грешка: Тази команда може да бъде използвана само от играта.'
error_invalid_version_uuid: '[Грешка:](#ff3300) [Не можахме да открием никакви потребителски данни за тази версия на това UUID.](#ff7e5e)' error_no_data_to_display: '[Грешка:](#ff3300) [Не можахме да открием никакви данни за потребителя, които да покажем.](#ff7e5e)'
husksync_command_description: 'Manage the HuskSync plugin' error_invalid_version_uuid: '[Грешка:](#ff3300) [Не можахме да открием никакви потребителски данни за тази версия на това UUID.](#ff7e5e)'
userdata_command_description: 'View, manage & restore player userdata' husksync_command_description: 'Manage the HuskSync plugin'
inventory_command_description: 'View & edit a player''s inventory' userdata_command_description: 'View, manage & restore player userdata'
enderchest_command_description: 'View & edit a player''s Ender Chest' inventory_command_description: 'View & edit a player''s inventory'
enderchest_command_description: 'View & edit a player''s Ender Chest'

View File

@@ -1,62 +1,63 @@
synchronization_complete: '[⏵ Daten synchronisiert!](#00fb9a)' locales:
synchronization_failed: '[⏵ Ein Fehler ist beim Synchronisieren deiner Daten aufgetreten! Bitte kontaktiere einen Administrator.](#ff7e5e)' synchronization_complete: '[⏵ Daten synchronisiert!](#00fb9a)'
inventory_viewer_menu_title: '&0Inventar von %1%' synchronization_failed: '[⏵ Ein Fehler ist beim Synchronisieren deiner Daten aufgetreten! Bitte kontaktiere einen Administrator.](#ff7e5e)'
ender_chest_viewer_menu_title: '&0Endertruhe von %1%' inventory_viewer_menu_title: '&0Inventar von %1%'
inventory_viewer_opened: '[Du siehst den Schnappschuss des Inventares von](#00fb9a) [%1%](#00fb9a bold) [von ⌚ %2%](#00fb9a)' ender_chest_viewer_menu_title: '&0Endertruhe von %1%'
ender_chest_viewer_opened: '[Du siehst den Schnappschuss der Endertruhe von](#00fb9a) [%1%](#00fb9a bold) [von ⌚ %2%](#00fb9a)' inventory_viewer_opened: '[Du siehst den Schnappschuss des Inventares von](#00fb9a) [%1%](#00fb9a bold) [von ⌚ %2%](#00fb9a)'
data_update_complete: '[🔔 Deine Daten wurden aktualisiert!](#00fb9a)' ender_chest_viewer_opened: '[Du siehst den Schnappschuss der Endertruhe von](#00fb9a) [%1%](#00fb9a bold) [von ⌚ %2%](#00fb9a)'
data_update_failed: '[🔔 Ein Fehler ist beim Aktualisieren deiner Daten aufgetreten! Bitte kontaktiere einen Administrator.](#ff7e5e)' data_update_complete: '[🔔 Deine Daten wurden aktualisiert!](#00fb9a)'
user_registration_complete: '[⭐ Benutzer-Registrierung abgeschlossen!](#00fb9a)' data_update_failed: '[🔔 Ein Fehler ist beim Aktualisieren deiner Daten aufgetreten! Bitte kontaktiere einen Administrator.](#ff7e5e)'
data_manager_title: '[Du siehst den Nutzerdaten-Schnappschuss](#00fb9a) [%1%](#00fb9a show_text=&7Versions-UUID:\n&8%2%) [für %3%](#00fb9a bold show_text=&7Spieler-UUID:\n&8%4%)[:](#00fb9a)' user_registration_complete: '[⭐ Benutzer-Registrierung abgeschlossen!](#00fb9a)'
data_manager_timestamp: '[⌚ %1%](#ffc43b-#f5c962 show_text=&7Versions-Zeitstempel:\n&8Zeitpunkt der Speicherung der Daten)' data_manager_title: '[Du siehst den Nutzerdaten-Schnappschuss](#00fb9a) [%1%](#00fb9a show_text=&7Versions-UUID:\n&8%2%) [für %3%](#00fb9a bold show_text=&7Spieler-UUID:\n&8%4%)[:](#00fb9a)'
data_manager_pinned: '[※ Schnappschuss angeheftet](#d8ff2b show_text=&7Angeheftet:\n&8Dieser Nutzerdaten-Schnappschuss wird nicht automatisch rotiert.)' data_manager_timestamp: '[⌚ %1%](#ffc43b-#f5c962 show_text=&7Versions-Zeitstempel:\n&8Zeitpunkt der Speicherung der Daten)'
data_manager_cause: '[⚑ %1%](#23a825-#36f539 show_text=&7Speicherungsgrund:\n&8Der Grund für das Speichern der Daten)' data_manager_pinned: '[※ Schnappschuss angeheftet](#d8ff2b show_text=&7Angeheftet:\n&8Dieser Nutzerdaten-Schnappschuss wird nicht automatisch rotiert.)'
data_manager_server: '[ %1%](#ff87b3-#f5538e show_text=&7Server:\n&8Name des Servers, auf dem die Daten gespeichert wurden)' data_manager_cause: '[ %1%](#23a825-#36f539 show_text=&7Speicherungsgrund:\n&8Der Grund für das Speichern der Daten)'
data_manager_size: '[ %1%](color=#62a9f5-#7ab8fa show_text=&7Schnappschuss-Größe:\n&8Geschätzte Dateigröße des Schnappschusses (in KiB))\n' data_manager_server: '[ %1%](#ff87b3-#f5538e show_text=&7Server:\n&8Name des Servers, auf dem die Daten gespeichert wurden)'
data_manger_status: '[%1%](red)[/](gray)[%2%](red)[×](gray)[❤](red show_text=&7Lebenspunkte) [%3%](yellow)[×](gray)[🍖](yellow show_text=&7Hungerpunkte) [ʟᴠ](green)[.](gray)[%4%](green show_text=&7XP-Level) [🏹 %5%](dark_aqua show_text=&7Spielmodus)' data_manager_size: '[%1%](color=#62a9f5-#7ab8fa show_text=&7Schnappschuss-Größe:\n&8Geschätzte Dateigröße des Schnappschusses (in KiB))\n'
data_manager_advancements_statistics: '[⭐ Erfolge: %1%](color=#ffc43b-#f5c962 show_text=&7Erfolge in denen du Fortschritt gemacht hast:\n&8%2%) [⌛ Spielzeit: %3%ʜʀs](color=#62a9f5-#7ab8fa show_text=&7Deine verbrachte Zeit im Spiel\n&8⚠ Basierend auf Spielstatistiken)\n' data_manger_status: '[%1%](red)[/](gray)[%2%](red)[×](gray)[❤](red show_text=&7Lebenspunkte) [%3%](yellow)[×](gray)[🍖](yellow show_text=&7Hungerpunkte) [ʟᴠ](green)[.](gray)[%4%](green show_text=&7XP-Level) [🏹 %5%](dark_aqua show_text=&7Spielmodus)'
data_manager_item_buttons: '[Sehen:](gray) [[🪣 Inventar…]](color=#a17b5f-#f5b98c show_text=&7Klicke zum Ansehen run_command=/inventory %1% %2%) [[⌀ Endertruhe…]](#b649c4-#d254ff show_text=&7Klicke zum Ansehen run_command=/enderchest %1% %2%)' data_manager_advancements_statistics: '[⭐ Erfolge: %1%](color=#ffc43b-#f5c962 show_text=&7Erfolge in denen du Fortschritt gemacht hast:\n&8%2%) [⌛ Spielzeit: %3%ʜʀs](color=#62a9f5-#7ab8fa show_text=&7Deine verbrachte Zeit im Spiel\n&8⚠ Basierend auf Spielstatistiken)\n'
data_manager_management_buttons: '[Verwalten:](gray) [[❌ Löschen…]](#ff3300 show_text=&7Klicke, um diesen Nutzerdaten-Schnappschuss zu löschen.\n&8Dies betrifft nicht die aktuellen Nutzerdaten.\n&#ff3300&⚠ Dieser Schritt kann nicht rückgängig gemacht werden! suggest_command=/husksync:userdata delete %1% %2%) [[⏪ Wiederherstellen…]](#00fb9a show_text=&7Klicke, um die Nutzerdaten wiederherzustellen.\n&8Dies wird die Nutzerdaten auf den Stand des Schnappschusses setzen.\n&#ff3300&⚠ Die aktuellen Nutzerdaten von %1% werden überschrieben! suggest_command=/husksync:userdata restore %1% %2%) [[※ Anheften/Loslösen…]](#d8ff2b show_text=&7Klicke, um diesen Nutzerdaten-Schnappschuss anzuheften oder loszulösen\n&8Angeheftete Nutzerdaten-Schnappschüsse werden nicht automatisch rotiert run_command=/userdata pin %1% %2%)' data_manager_item_buttons: '[Sehen:](gray) [[🪣 Inventar…]](color=#a17b5f-#f5b98c show_text=&7Klicke zum Ansehen run_command=/inventory %1% %2%) [[⌀ Endertruhe…]](#b649c4-#d254ff show_text=&7Klicke zum Ansehen run_command=/enderchest %1% %2%)'
data_manager_system_buttons: '[System:](gray) [[⏷ Daten-Dump…]](dark_gray show_text=&7Klicke, um diesen rohen Nutzerdaten-Schnappschuss in eine Datei zu speichern.\n&8Daten-Dumps können unter ~/plugins/HuskSync/dumps/ gefunden werden. run_command=/husksync:userdata dump %1% %2% file) [[☂ Web-Dump…]](dark_gray show_text=&7Klicke, um diesen rohen Nutzerdaten-Schnappschuss auf den mc-logs Service hochzuladen.\n&8Du erhältst dann eine URL, die die Daten enthält. run_command=/husksync:userdata dump %1% %2% web)' data_manager_management_buttons: '[Verwalten:](gray) [[❌ Löschen…]](#ff3300 show_text=&7Klicke, um diesen Nutzerdaten-Schnappschuss zu löschen.\n&8Dies betrifft nicht die aktuellen Nutzerdaten.\n&#ff3300&⚠ Dieser Schritt kann nicht rückgängig gemacht werden! suggest_command=/husksync:userdata delete %1% %2%) [[⏪ Wiederherstellen…]](#00fb9a show_text=&7Klicke, um die Nutzerdaten wiederherzustellen.\n&8Dies wird die Nutzerdaten auf den Stand des Schnappschusses setzen.\n&#ff3300&⚠ Die aktuellen Nutzerdaten von %1% werden überschrieben! suggest_command=/husksync:userdata restore %1% %2%) [[※ Anheften/Loslösen…]](#d8ff2b show_text=&7Klicke, um diesen Nutzerdaten-Schnappschuss anzuheften oder loszulösen\n&8Angeheftete Nutzerdaten-Schnappschüsse werden nicht automatisch rotiert run_command=/userdata pin %1% %2%)'
data_manager_advancements_preview_remaining: 'und %1% weitere…' data_manager_system_buttons: '[System:](gray) [[⏷ Daten-Dump…]](dark_gray show_text=&7Klicke, um diesen rohen Nutzerdaten-Schnappschuss in eine Datei zu speichern.\n&8Daten-Dumps können unter ~/plugins/HuskSync/dumps/ gefunden werden. run_command=/husksync:userdata dump %1% %2% file) [[☂ Web-Dump…]](dark_gray show_text=&7Klicke, um diesen rohen Nutzerdaten-Schnappschuss auf den mc-logs Service hochzuladen.\n&8Du erhältst dann eine URL, die die Daten enthält. run_command=/husksync:userdata dump %1% %2% web)'
data_list_title: '[Nutzerdaten-Schnappschüsse von %1%:](#00fb9a) [(%2%-%3% von](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n' data_manager_advancements_preview_remaining: 'und %1% weitere…'
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_title: '[Nutzerdaten-Schnappschüsse von %1%:](#00fb9a) [(%2%-%3% von](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n'
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_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_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_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_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%)' 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_unpinned: '[※ Nutzerdaten-Schnappschuss erfolgreich losgelöst](#00fb9a) [%1%](#00fb9a show_text=&7Versions-UUID:\n&8%2%) [für](#00fb9a) [%3%.](#00fb9a show_text=&7Spieler-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%)'
data_dumped: '[ Nutzerdaten-Schnappschuss %1% für %2% erfolgreich gedumpt nach:](#00fb9a) &7%3%' data_unpinned: '[ Nutzerdaten-Schnappschuss erfolgreich losgelöst](#00fb9a) [%1%](#00fb9a show_text=&7Versions-UUID:\n&8%2%) [für](#00fb9a) [%3%.](#00fb9a show_text=&7Spieler-UUID:\n&8%4%)'
list_footer: '\n%1%[Seite](#00fb9a) [%2%](#00fb9a)/[%3%](#00fb9a)%4% %5%' data_dumped: '[☂ Nutzerdaten-Schnappschuss %1% für %2% erfolgreich gedumpt nach:](#00fb9a) &7%3%'
list_previous_page_button: '[◀](white show_text=&7Siehe vorherige Seite run_command=%2% %1%) ' list_footer: '\n%1%[Seite](#00fb9a) [%2%](#00fb9a)/[%3%](#00fb9a)%4% %5%'
list_next_page_button: ' [▶](white show_text=&7Siehe nächste Seite run_command=%2% %1%)' list_previous_page_button: '[◀](white show_text=&7Siehe vorherige Seite run_command=%2% %1%) '
list_page_jumpers: '(%1%)' list_next_page_button: ' [▶](white show_text=&7Siehe nächste Seite run_command=%2% %1%)'
list_page_jumper_button: '[%1%](show_text=&7Springe zu Seite %1% run_command=%2% %1%)' list_page_jumpers: '(%1%)'
list_page_jumper_current_page: '[%1%](#00fb9a)' list_page_jumper_button: '[%1%](show_text=&7Springe zu Seite %1% run_command=%2% %1%)'
list_page_jumper_separator: ' ' list_page_jumper_current_page: '[%1%](#00fb9a)'
list_page_jumper_group_separator: '' list_page_jumper_separator: ' '
save_cause_disconnect: 'Server verlassen' list_page_jumper_group_separator: '…'
save_cause_world_save: 'Welt gespeichert' save_cause_disconnect: 'Server verlassen'
save_cause_death: 'Tod' save_cause_world_save: 'Welt gespeichert'
save_cause_server_shutdown: 'Server gestoppt' save_cause_death: 'Tod'
save_cause_inventory_command: 'Inventar Befehl' save_cause_server_shutdown: 'Server gestoppt'
save_cause_enderchest_command: 'Enderchest Befehl' save_cause_inventory_command: 'Inventar Befehl'
save_cause_backup_restore: 'Backup wiederhergestellt' save_cause_enderchest_command: 'Enderchest Befehl'
save_cause_api: 'API' save_cause_backup_restore: 'Backup wiederhergestellt'
save_cause_mpdb_migration: 'MPDB Migration' save_cause_api: 'API'
save_cause_legacy_migration: 'Legacy Migration' save_cause_mpdb_migration: 'MPDB Migration'
save_cause_converted_from_v2: 'Import von v2' save_cause_legacy_migration: 'Legacy Migration'
reload_complete: '[HuskSync](#00fb9a bold) [| Die Konfigurations- und Sprachdateien wurden neu geladen.](#00fb9a)\n[⚠ Stelle sicher, dass die Konfigurationsdateien auf allen Servern aktuell sind!](#00fb9a)\n[Ein Neustart wird benötigt, damit Konfigurations-Änderungen wirkbar werden.](#00fb9a italic)' save_cause_converted_from_v2: 'Import von v2'
up_to_date: '[HuskSync](#00fb9a bold) [| Du verwendest die neuste Version von HuskSync (v%1%).](#00fb9a)' reload_complete: '[HuskSync](#00fb9a bold) [| Die Konfigurations- und Sprachdateien wurden neu geladen.](#00fb9a)\n[⚠ Stelle sicher, dass die Konfigurationsdateien auf allen Servern aktuell sind!](#00fb9a)\n[Ein Neustart wird benötigt, damit Konfigurations-Änderungen wirkbar werden.](#00fb9a italic)'
update_available: '[HuskSync](#ff7e5e bold) [| Eine neue Version von HuskSync ist verfügbar: v%1% (Aktuelle Version: v%2%).](#ff7e5e)' up_to_date: '[HuskSync](#00fb9a bold) [| Du verwendest die neuste Version von HuskSync (v%1%).](#00fb9a)'
system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)' update_available: '[HuskSync](#ff7e5e bold) [| Eine neue Version von HuskSync ist verfügbar: v%1% (Aktuelle Version: v%2%).](#ff7e5e)'
error_invalid_syntax: '[Fehler:](#ff3300) [Falsche Syntax. Nutze:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)' system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)'
error_invalid_player: '[Fehler:](#ff3300) [Es konnte kein Spieler mit diesem Namen gefunden werden.](#ff7e5e)' error_invalid_syntax: '[Fehler:](#ff3300) [Falsche Syntax. Nutze:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)'
error_no_permission: '[Fehler:](#ff3300) [Du hast nicht die benötigten Berechtigungen um diesen Befehl auszuführen](#ff7e5e)' error_invalid_player: '[Fehler:](#ff3300) [Es konnte kein Spieler mit diesem Namen gefunden werden.](#ff7e5e)'
error_console_command_only: '[Fehler:](#ff3300) [Dieser Befehl kann nur über die Konsole ausgeführt werden.](#ff7e5e)' error_no_permission: '[Fehler:](#ff3300) [Du hast nicht die benötigten Berechtigungen um diesen Befehl auszuführen](#ff7e5e)'
error_in_game_command_only: 'Fehler: Dieser Befehl kann nur im Spiel genutzt werden.' error_console_command_only: '[Fehler:](#ff3300) [Dieser Befehl kann nur über die Konsole ausgeführt werden.](#ff7e5e)'
error_no_data_to_display: '[Fehler:](#ff3300) [Es konnten keine Nutzerdaten zum Anzeigen gefunden werden.](#ff7e5e)' error_in_game_command_only: 'Fehler: Dieser Befehl kann nur im Spiel genutzt werden.'
error_invalid_version_uuid: '[Fehler:](#ff3300) [Es konnten keine Nutzerdaten für diese Versions-UUID gefunden werden.](#ff7e5e)' error_no_data_to_display: '[Fehler:](#ff3300) [Es konnten keine Nutzerdaten zum Anzeigen gefunden werden.](#ff7e5e)'
husksync_command_description: 'Das HuskSync-Plugin verwalten' error_invalid_version_uuid: '[Fehler:](#ff3300) [Es konnten keine Nutzerdaten für diese Versions-UUID gefunden werden.](#ff7e5e)'
userdata_command_description: 'Nutzerdaten eines Spielers anzeigen, verwalten und wiederherstellen' husksync_command_description: 'Das HuskSync-Plugin verwalten'
inventory_command_description: 'Inventar eines Spielers ansehen und bearbeiten' userdata_command_description: 'Nutzerdaten eines Spielers anzeigen, verwalten und wiederherstellen'
enderchest_command_description: 'Endertruhe eines Spielers ansehen und bearbeiten' inventory_command_description: 'Inventar eines Spielers ansehen und bearbeiten'
enderchest_command_description: 'Endertruhe eines Spielers ansehen und bearbeiten'

View File

@@ -1,62 +1,63 @@
synchronization_complete: '[⏵ Data synchronized!](#00fb9a)' locales:
synchronization_failed: '[⏵ Failed to synchronize your data! Please contact an administrator.](#ff7e5e)' synchronization_complete: '[⏵ Data synchronized!](#00fb9a)'
inventory_viewer_menu_title: '&0%1%''s Inventory' synchronization_failed: '[⏵ Failed to synchronize your data! Please contact an administrator.](#ff7e5e)'
ender_chest_viewer_menu_title: '&0%1%''s Ender Chest' inventory_viewer_menu_title: '&0%1%''s Inventory'
inventory_viewer_opened: '[Viewing snapshot of](#00fb9a) [%1%](#00fb9a bold)[''s inventory as of ⌚ %2%](#00fb9a)' ender_chest_viewer_menu_title: '&0%1%''s Ender Chest'
ender_chest_viewer_opened: '[Viewing snapshot of](#00fb9a) [%1%](#00fb9a bold)[''s Ender Chest as of ⌚ %2%](#00fb9a)' inventory_viewer_opened: '[Viewing snapshot of](#00fb9a) [%1%](#00fb9a bold)[''s inventory as of ⌚ %2%](#00fb9a)'
data_update_complete: '[🔔 Your data has been updated!](#00fb9a)' ender_chest_viewer_opened: '[Viewing snapshot of](#00fb9a) [%1%](#00fb9a bold)[''s Ender Chest as of ⌚ %2%](#00fb9a)'
data_update_failed: '[🔔 Failed to update your data! Please contact an administrator.](#ff7e5e)' data_update_complete: '[🔔 Your data has been updated!](#00fb9a)'
user_registration_complete: '[⭐ User registration complete!](#00fb9a)' data_update_failed: '[🔔 Failed to update your data! Please contact an administrator.](#ff7e5e)'
data_manager_title: '[Viewing user data snapshot](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\n&8%2%) [for](#00fb9a) [%3%](#00fb9a bold show_text=&7Player UUID:\n&8%4%)[:](#00fb9a)' user_registration_complete: '[⭐ User registration complete!](#00fb9a)'
data_manager_timestamp: '[⌚ %1%](#ffc43b-#f5c962 show_text=&7Version timestamp:\n&8When the data was saved)' data_manager_title: '[Viewing user data snapshot](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\n&8%2%) [for](#00fb9a) [%3%](#00fb9a bold show_text=&7Player UUID:\n&8%4%)[:](#00fb9a)'
data_manager_pinned: '[※ Snapshot pinned](#d8ff2b show_text=&7Pinned:\n&8This user data snapshot won''t be automatically rotated.)' data_manager_timestamp: '[⌚ %1%](#ffc43b-#f5c962 show_text=&7Version timestamp:\n&8When the data was saved)'
data_manager_cause: '[⚑ %1%](#23a825-#36f539 show_text=&7Save cause:\n&8What caused the data to be saved)' data_manager_pinned: '[※ Snapshot pinned](#d8ff2b show_text=&7Pinned:\n&8This user data snapshot won''t be automatically rotated.)'
data_manager_server: '[ %1%](#ff87b3-#f5538e show_text=&7Server:\n&8Name of the server the data was saved on)' data_manager_cause: '[ %1%](#23a825-#36f539 show_text=&7Save cause:\n&8What caused the data to be saved)'
data_manager_size: '[ %1%](color=#62a9f5-#7ab8fa show_text=&7Snapshot size:\n&8Estimated file size of the snapshot (in KiB))\n' data_manager_server: '[ %1%](#ff87b3-#f5538e show_text=&7Server:\n&8Name of the server the data was saved on)'
data_manger_status: '[%1%](red)[/](gray)[%2%](red)[×](gray)[❤](red show_text=&7Health points) [%3%](yellow)[×](gray)[🍖](yellow show_text=&7Hunger points) [ʟᴠ](green)[.](gray)[%4%](green show_text=&7XP level) [🏹 %5%](dark_aqua show_text=&7Game mode)' data_manager_size: '[%1%](color=#62a9f5-#7ab8fa show_text=&7Snapshot size:\n&8Estimated file size of the snapshot (in KiB))\n'
data_manager_advancements_statistics: '[⭐ Advancements: %1%](color=#ffc43b-#f5c962 show_text=&7Advancements you have progress in:\n&8%2%) [⌛ Play Time: %3%ʜʀs](color=#62a9f5-#7ab8fa show_text=&7In-game play time\n&8⚠ Based on in-game statistics)\n' data_manger_status: '[%1%](red)[/](gray)[%2%](red)[×](gray)[❤](red show_text=&7Health points) [%3%](yellow)[×](gray)[🍖](yellow show_text=&7Hunger points) [ʟᴠ](green)[.](gray)[%4%](green show_text=&7XP level) [🏹 %5%](dark_aqua show_text=&7Game mode)'
data_manager_item_buttons: '[View:](gray) [[🪣 Inventory…]](color=#a17b5f-#f5b98c show_text=&7Click to view run_command=/inventory %1% %2%) [[⌀ Ender Chest…]](#b649c4-#d254ff show_text=&7Click to view run_command=/enderchest %1% %2%)' data_manager_advancements_statistics: '[⭐ Advancements: %1%](color=#ffc43b-#f5c962 show_text=&7Advancements you have progress in:\n&8%2%) [⌛ Play Time: %3%ʜʀs](color=#62a9f5-#7ab8fa show_text=&7In-game play time\n&8⚠ Based on in-game statistics)\n'
data_manager_management_buttons: '[Manage:](gray) [[❌ Delete…]](#ff3300 show_text=&7Click to delete this snapshot of user data.\n&8This will not affect the user''s current data.\n&#ff3300&⚠ This cannot be undone! suggest_command=/husksync:userdata delete %1% %2%) [[⏪ Restore…]](#00fb9a show_text=&7Click to restore this user data.\n&8This will set the user''s data to this snapshot.\n&#ff3300&⚠ %1%''s current data will be overwritten! suggest_command=/husksync:userdata restore %1% %2%) [[※ Pin/Unpin…]](#d8ff2b show_text=&7Click to pin or unpin this user data snapshot\n&8Pinned snapshots won''t be automatically rotated run_command=/userdata pin %1% %2%)' data_manager_item_buttons: '[View:](gray) [[🪣 Inventory…]](color=#a17b5f-#f5b98c show_text=&7Click to view run_command=/inventory %1% %2%) [[⌀ Ender Chest…]](#b649c4-#d254ff show_text=&7Click to view run_command=/enderchest %1% %2%)'
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_management_buttons: '[Manage:](gray) [[❌ Delete…]](#ff3300 show_text=&7Click to delete this snapshot of user data.\n&8This will not affect the user''s current data.\n&#ff3300&⚠ This cannot be undone! suggest_command=/husksync:userdata delete %1% %2%) [[⏪ Restore…]](#00fb9a show_text=&7Click to restore this user data.\n&8This will set the user''s data to this snapshot.\n&#ff3300&⚠ %1%''s current data will be overwritten! suggest_command=/husksync:userdata restore %1% %2%) [[※ Pin/Unpin…]](#d8ff2b show_text=&7Click to pin or unpin this user data snapshot\n&8Pinned snapshots won''t be automatically rotated run_command=/userdata pin %1% %2%)'
data_manager_advancements_preview_remaining: 'and %1% more…' 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_list_title: '[%1%''s user data snapshots:](#00fb9a) [(%2%-%3% of](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n' data_manager_advancements_preview_remaining: 'and %1% more…'
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_title: '[%1%''s user data snapshots:](#00fb9a) [(%2%-%3% of](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n'
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_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_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_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_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%)' 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_unpinned: '[※ Successfully unpinned 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_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%)'
data_dumped: '[ Successfully dumped the user data snapshot %1% for %2% to:](#00fb9a) &7%3%' data_unpinned: '[ Successfully unpinned user data snapshot](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\n&8%2%) [for](#00fb9a) [%3%.](#00fb9a show_text=&7Player UUID:\n&8%4%)'
list_footer: '\n%1%[Page](#00fb9a) [%2%](#00fb9a)/[%3%](#00fb9a)%4% %5%' data_dumped: '[☂ Successfully dumped the user data snapshot %1% for %2% to:](#00fb9a) &7%3%'
list_previous_page_button: '[◀](white show_text=&7View previous page run_command=%2% %1%) ' list_footer: '\n%1%[Page](#00fb9a) [%2%](#00fb9a)/[%3%](#00fb9a)%4% %5%'
list_next_page_button: ' [▶](white show_text=&7View next page run_command=%2% %1%)' list_previous_page_button: '[◀](white show_text=&7View previous page run_command=%2% %1%) '
list_page_jumpers: '(%1%)' list_next_page_button: ' [▶](white show_text=&7View next page run_command=%2% %1%)'
list_page_jumper_button: '[%1%](show_text=&7Jump to page %1% run_command=%2% %1%)' list_page_jumpers: '(%1%)'
list_page_jumper_current_page: '[%1%](#00fb9a)' list_page_jumper_button: '[%1%](show_text=&7Jump to page %1% run_command=%2% %1%)'
list_page_jumper_separator: ' ' list_page_jumper_current_page: '[%1%](#00fb9a)'
list_page_jumper_group_separator: '' list_page_jumper_separator: ' '
save_cause_disconnect: 'disconnect' list_page_jumper_group_separator: '…'
save_cause_world_save: 'world save' save_cause_disconnect: 'disconnect'
save_cause_death: 'death' save_cause_world_save: 'world save'
save_cause_server_shutdown: 'server shutdown' save_cause_death: 'death'
save_cause_inventory_command: 'inventory command' save_cause_server_shutdown: 'server shutdown'
save_cause_enderchest_command: 'enderchest command' save_cause_inventory_command: 'inventory command'
save_cause_backup_restore: 'backup restore' save_cause_enderchest_command: 'enderchest command'
save_cause_api: 'API' save_cause_backup_restore: 'backup restore'
save_cause_mpdb_migration: 'MPDB migration' save_cause_api: 'API'
save_cause_legacy_migration: 'legacy migration' save_cause_mpdb_migration: 'MPDB migration'
save_cause_converted_from_v2: 'converted from v2' save_cause_legacy_migration: 'legacy migration'
up_to_date: '[HuskSync](#00fb9a bold) [| You are running the latest version of HuskSync (v%1%).](#00fb9a)' save_cause_converted_from_v2: 'converted from v2'
update_available: '[HuskSync](#ff7e5e bold) [| A new version of HuskSync is available: v%1% (running: v%2%).](#ff7e5e)' up_to_date: '[HuskSync](#00fb9a bold) [| You are running the latest version of HuskSync (v%1%).](#00fb9a)'
reload_complete: '[HuskSync](#00fb9a bold) [| Reloaded config and message files.](#00fb9a)\n[⚠ Ensure config files are up-to-date on all servers!](#00fb9a)\n[A restart is needed for config changes to take effect.](#00fb9a italic)' update_available: '[HuskSync](#ff7e5e bold) [| A new version of HuskSync is available: v%1% (running: v%2%).](#ff7e5e)'
system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)' reload_complete: '[HuskSync](#00fb9a bold) [| Reloaded config and message files.](#00fb9a)\n[⚠ Ensure config files are up-to-date on all servers!](#00fb9a)\n[A restart is needed for config changes to take effect.](#00fb9a italic)'
error_invalid_syntax: '[Error:](#ff3300) [Incorrect syntax. Usage:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)' system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)'
error_invalid_player: '[Error:](#ff3300) [Could not find a player by that name.](#ff7e5e)' error_invalid_syntax: '[Error:](#ff3300) [Incorrect syntax. Usage:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)'
error_no_permission: '[Error:](#ff3300) [You do not have permission to execute this command](#ff7e5e)' error_invalid_player: '[Error:](#ff3300) [Could not find a player by that name.](#ff7e5e)'
error_console_command_only: '[Error:](#ff3300) [That command can only be run through console](#ff7e5e)' error_no_permission: '[Error:](#ff3300) [You do not have permission to execute this command](#ff7e5e)'
error_in_game_command_only: 'Error: That command can only be used in-game.' error_console_command_only: '[Error:](#ff3300) [That command can only be run through console](#ff7e5e)'
error_no_data_to_display: '[Error:](#ff3300) [Could not find any user data to display.](#ff7e5e)' error_in_game_command_only: 'Error: That command can only be used in-game.'
error_invalid_version_uuid: '[Error:](#ff3300) [Could not find any user data for that version UUID.](#ff7e5e)' error_no_data_to_display: '[Error:](#ff3300) [Could not find any user data to display.](#ff7e5e)'
husksync_command_description: 'Manage the HuskSync plugin' error_invalid_version_uuid: '[Error:](#ff3300) [Could not find any user data for that version UUID.](#ff7e5e)'
userdata_command_description: 'View, manage & restore player userdata' husksync_command_description: 'Manage the HuskSync plugin'
inventory_command_description: 'View & edit a player''s inventory' userdata_command_description: 'View, manage & restore player userdata'
enderchest_command_description: 'View & edit a player''s Ender Chest' inventory_command_description: 'View & edit a player''s inventory'
enderchest_command_description: 'View & edit a player''s Ender Chest'

View File

@@ -1,62 +1,63 @@
synchronization_complete: '[⏵ ¡Datos sincronizados!](#00fb9a)' locales:
synchronization_failed: '[⏵ Fallo al sincronizar los datos, por favor, contacte con un administrador.](#ff7e5e)' synchronization_complete: '[⏵ ¡Datos sincronizados!](#00fb9a)'
inventory_viewer_menu_title: '&0%1% Inventario de:' synchronization_failed: '[⏵ Fallo al sincronizar los datos, por favor, contacte con un administrador.](#ff7e5e)'
ender_chest_viewer_menu_title: '&0%1% Enderchest de:' inventory_viewer_menu_title: '&0%1% Inventario de:'
inventory_viewer_opened: '[Viendo una snapshot de](#00fb9a) [%1%](#00fb9a bold) [Inventario a partir de ⌚ %2%](#00fb9a)' ender_chest_viewer_menu_title: '&0%1% Enderchest de:'
ender_chest_viewer_opened: '[Viendo una snapshot de](#00fb9a) [%1%](#00fb9a bold) [Enderchest a partir de ⌚ %2%](#00fb9a)' inventory_viewer_opened: '[Viendo una snapshot de](#00fb9a) [%1%](#00fb9a bold) [Inventario a partir de ⌚ %2%](#00fb9a)'
data_update_complete: '[🔔 ¡Tus datos han sido actualizados!](#00fb9a)' ender_chest_viewer_opened: '[Viendo una snapshot de](#00fb9a) [%1%](#00fb9a bold) [Enderchest a partir de ⌚ %2%](#00fb9a)'
data_update_failed: '[🔔 Error al actualizar tus datos, por favor, contacte con un administrador.](#ff7e5e)' data_update_complete: '[🔔 ¡Tus datos han sido actualizados!](#00fb9a)'
user_registration_complete: '[⭐ User registration complete!](#00fb9a)' data_update_failed: '[🔔 Error al actualizar tus datos, por favor, contacte con un administrador.](#ff7e5e)'
data_manager_title: '[Viendo una snapshot sobre la informacion del jugador](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\n&8%2%) [for](#00fb9a) [%3%](#00fb9a bold show_text=&7Player UUID:\n&8%4%)[:](#00fb9a)' user_registration_complete: '[⭐ User registration complete!](#00fb9a)'
data_manager_timestamp: '[⌚ %1%](#ffc43b-#f5c962 show_text=&7Version del registro:\n&8Cuando los datos se han guardado)' data_manager_title: '[Viendo una snapshot sobre la informacion del jugador](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\n&8%2%) [for](#00fb9a) [%3%](#00fb9a bold show_text=&7Player UUID:\n&8%4%)[:](#00fb9a)'
data_manager_pinned: '[※ Snapshot anclada](#d8ff2b show_text=&Anclado:\n&8La informacion de este jugador no se rotará automaticamente.)' data_manager_timestamp: '[⌚ %1%](#ffc43b-#f5c962 show_text=&7Version del registro:\n&8Cuando los datos se han guardado)'
data_manager_cause: '[⚑ %1%](#23a825-#36f539 show_text=&7Motivo del guardado:\n&8Lo que ha causado que se guarde)' data_manager_pinned: '[※ Snapshot anclada](#d8ff2b show_text=&Anclado:\n&8La informacion de este jugador no se rotará automaticamente.)'
data_manager_server: '[ %1%](#ff87b3-#f5538e show_text=&7Server:\n&8Name of the server the data was saved on)' data_manager_cause: '[ %1%](#23a825-#36f539 show_text=&7Motivo del guardado:\n&8Lo que ha causado que se guarde)'
data_manager_size: '[ %1%](color=#62a9f5-#7ab8fa show_text=&7Snapshot size:\n&8Estimated file size of the snapshot (in KiB))\n' data_manager_server: '[ %1%](#ff87b3-#f5538e show_text=&7Server:\n&8Name of the server the data was saved on)'
data_manger_status: '[%1%](red)[/](gray)[%2%](red)[×](gray)[❤](red show_text=&7Puntos de vida) [%3%](yellow)[×](gray)[🍖](yellow show_text=&7Puntos de hambre) [ʟᴠ](green)[.](gray)[%4%](green show_text=&7Nivel de exp) [🏹 %5%](dark_aqua show_text=&7Gamemode)' data_manager_size: '[%1%](color=#62a9f5-#7ab8fa show_text=&7Snapshot size:\n&8Estimated file size of the snapshot (in KiB))\n'
data_manager_advancements_statistics: '[⭐ Logros: %1%](color=#ffc43b-#f5c962 show_text=&7Logros que has conseguido:\n&8%2%) [⌛ Tiempo de juego: %3%ʜʀs](color=#62a9f5-#7ab8fa show_text=&7In-game play time\n&8⚠ Based on in-game statistics)\n' data_manger_status: '[%1%](red)[/](gray)[%2%](red)[×](gray)[❤](red show_text=&7Puntos de vida) [%3%](yellow)[×](gray)[🍖](yellow show_text=&7Puntos de hambre) [ʟᴠ](green)[.](gray)[%4%](green show_text=&7Nivel de exp) [🏹 %5%](dark_aqua show_text=&7Gamemode)'
data_manager_item_buttons: '[View:](gray) [[🪣 Inventario…]](color=#a17b5f-#f5b98c show_text=&7Click para ver run_command=/inventory %1% %2%) [[⌀ Enderchest…]](#b649c4-#d254ff show_text=&7Click para ver run_command=/enderchest %1% %2%)' data_manager_advancements_statistics: '[⭐ Logros: %1%](color=#ffc43b-#f5c962 show_text=&7Logros que has conseguido:\n&8%2%) [⌛ Tiempo de juego: %3%ʜʀs](color=#62a9f5-#7ab8fa show_text=&7In-game play time\n&8⚠ Based on in-game statistics)\n'
data_manager_management_buttons: '[Manage:](gray) [[❌ Borrar…]](#ff3300 show_text=&7Click para borrar la snapshot del usuario.\n&8Esto no afectará a la informacion actual del jugador.\n&#ff3300&⚠ ¡Esto no se puede deshacer! suggest_command=/husksync:userdata delete %1% %2%) [[⏪ Restaurar…]](#00fb9a show_text=&7Click para restaurar la informacion de este usuario.\n&8Esto hará que la informacion actual cambie por esta snapshot.\n&#ff3300&⚠ %1% la informacion actual será sustituida! suggest_command=/husksync:userdata restore %1% %2%) [[※ Pin/Unpin…]](#d8ff2b show_text=&7Click para anclar/desanclar esta snapshot\n&8Las snapshot ancladas no seran rotadas automaticamente run_command=/userdata pin %1% %2%)' data_manager_item_buttons: '[View:](gray) [[🪣 Inventario…]](color=#a17b5f-#f5b98c show_text=&7Click para ver run_command=/inventory %1% %2%) [[⌀ Enderchest…]](#b649c4-#d254ff show_text=&7Click para ver run_command=/enderchest %1% %2%)'
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_management_buttons: '[Manage:](gray) [[❌ Borrar…]](#ff3300 show_text=&7Click para borrar la snapshot del usuario.\n&8Esto no afectará a la informacion actual del jugador.\n&#ff3300&⚠ ¡Esto no se puede deshacer! suggest_command=/husksync:userdata delete %1% %2%) [[⏪ Restaurar…]](#00fb9a show_text=&7Click para restaurar la informacion de este usuario.\n&8Esto hará que la informacion actual cambie por esta snapshot.\n&#ff3300&⚠ %1% la informacion actual será sustituida! suggest_command=/husksync:userdata restore %1% %2%) [[※ Pin/Unpin…]](#d8ff2b show_text=&7Click para anclar/desanclar esta snapshot\n&8Las snapshot ancladas no seran rotadas automaticamente run_command=/userdata pin %1% %2%)'
data_manager_advancements_preview_remaining: 'y %1% más…' 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_list_title: '[%1%''s user data snapshots:](#00fb9a) [(%2%-%3% of](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n' data_manager_advancements_preview_remaining: 'y %1% más…'
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_title: '[%1%''s user data snapshots:](#00fb9a) [(%2%-%3% of](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n'
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_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_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_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_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%)' 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_unpinned: '[※ Se ha desanclado 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%)' 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%)'
data_dumped: '[ Successfully dumped the user data snapshot %1% for %2% to:](#00fb9a) &7%3%' data_unpinned: '[ Se ha desanclado 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%)'
list_footer: '\n%1%[Page](#00fb9a) [%2%](#00fb9a)/[%3%](#00fb9a)%4% %5%' data_dumped: '[☂ Successfully dumped the user data snapshot %1% for %2% to:](#00fb9a) &7%3%'
list_previous_page_button: '[◀](white show_text=&7View previous page run_command=%2% %1%) ' list_footer: '\n%1%[Page](#00fb9a) [%2%](#00fb9a)/[%3%](#00fb9a)%4% %5%'
list_next_page_button: ' [▶](white show_text=&7View next page run_command=%2% %1%)' list_previous_page_button: '[◀](white show_text=&7View previous page run_command=%2% %1%) '
list_page_jumpers: '(%1%)' list_next_page_button: ' [▶](white show_text=&7View next page run_command=%2% %1%)'
list_page_jumper_button: '[%1%](show_text=&7Jump to page %1% run_command=%2% %1%)' list_page_jumpers: '(%1%)'
list_page_jumper_current_page: '[%1%](#00fb9a)' list_page_jumper_button: '[%1%](show_text=&7Jump to page %1% run_command=%2% %1%)'
list_page_jumper_separator: ' ' list_page_jumper_current_page: '[%1%](#00fb9a)'
list_page_jumper_group_separator: '' list_page_jumper_separator: ' '
save_cause_disconnect: 'disconnect' list_page_jumper_group_separator: '…'
save_cause_world_save: 'world save' save_cause_disconnect: 'disconnect'
save_cause_death: 'death' save_cause_world_save: 'world save'
save_cause_server_shutdown: 'server shutdown' save_cause_death: 'death'
save_cause_inventory_command: 'inventory command' save_cause_server_shutdown: 'server shutdown'
save_cause_enderchest_command: 'enderchest command' save_cause_inventory_command: 'inventory command'
save_cause_backup_restore: 'backup restore' save_cause_enderchest_command: 'enderchest command'
save_cause_api: 'API' save_cause_backup_restore: 'backup restore'
save_cause_mpdb_migration: 'MPDB migration' save_cause_api: 'API'
save_cause_legacy_migration: 'legacy migration' save_cause_mpdb_migration: 'MPDB migration'
save_cause_converted_from_v2: 'converted from v2' save_cause_legacy_migration: 'legacy migration'
up_to_date: '[HuskSync](#00fb9a bold) [| You are running the latest version of HuskSync (v%1%).](#00fb9a)' save_cause_converted_from_v2: 'converted from v2'
update_available: '[HuskSync](#ff7e5e bold) [| A new version of HuskSync is available: v%1% (running: v%2%).](#ff7e5e)' up_to_date: '[HuskSync](#00fb9a bold) [| You are running the latest version of HuskSync (v%1%).](#00fb9a)'
reload_complete: '[HuskSync](#00fb9a bold) [| Recargada la configuración y los archivos de lenguaje.](#00fb9a)\n[⚠ Ensure config files are up-to-date on all servers!](#00fb9a)\n[A restart is needed for config changes to take effect.](#00fb9a italic)' update_available: '[HuskSync](#ff7e5e bold) [| A new version of HuskSync is available: v%1% (running: v%2%).](#ff7e5e)'
system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)' reload_complete: '[HuskSync](#00fb9a bold) [| Recargada la configuración y los archivos de lenguaje.](#00fb9a)\n[⚠ Ensure config files are up-to-date on all servers!](#00fb9a)\n[A restart is needed for config changes to take effect.](#00fb9a italic)'
error_invalid_syntax: '[Error:](#ff3300) [Sintanxis incorrecta. Usa:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)' system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)'
error_invalid_player: '[Error:](#ff3300) [No se ha podido encontrar un jugador con ese nombre.](#ff7e5e)' error_invalid_syntax: '[Error:](#ff3300) [Sintanxis incorrecta. Usa:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)'
error_no_permission: '[Error:](#ff3300) [No tienes permisos para ejecutar este comando.](#ff7e5e)' error_invalid_player: '[Error:](#ff3300) [No se ha podido encontrar un jugador con ese nombre.](#ff7e5e)'
error_console_command_only: '[Error:](#ff3300) [Este comando solo se puede ejecutar desde la consola.](#ff7e5e)' error_no_permission: '[Error:](#ff3300) [No tienes permisos para ejecutar este comando.](#ff7e5e)'
error_in_game_command_only: 'Error: Ese comando solo se puede utilizar desde el juego.' error_console_command_only: '[Error:](#ff3300) [Este comando solo se puede ejecutar desde la consola.](#ff7e5e)'
error_no_data_to_display: '[Error:](#ff3300) [No se ha podido encontrar informacion sobre el jugador.](#ff7e5e)' error_in_game_command_only: 'Error: Ese comando solo se puede utilizar desde el juego.'
error_invalid_version_uuid: '[Error:](#ff3300) [No se ha podido encontrar informacion sobre la UUID de ese jugador.](#ff7e5e)' error_no_data_to_display: '[Error:](#ff3300) [No se ha podido encontrar informacion sobre el jugador.](#ff7e5e)'
husksync_command_description: 'Manage the HuskSync plugin' error_invalid_version_uuid: '[Error:](#ff3300) [No se ha podido encontrar informacion sobre la UUID de ese jugador.](#ff7e5e)'
userdata_command_description: 'View, manage & restore player userdata' husksync_command_description: 'Manage the HuskSync plugin'
inventory_command_description: 'View & edit a player''s inventory' userdata_command_description: 'View, manage & restore player userdata'
enderchest_command_description: 'View & edit a player''s Ender Chest' inventory_command_description: 'View & edit a player''s inventory'
enderchest_command_description: 'View & edit a player''s Ender Chest'

View File

@@ -0,0 +1,63 @@
locales:
synchronization_complete: '[⏵ Data disinkronkan!](#00fb9a)'
synchronization_failed: '[⏵ Gagal menyinkronkan datamu! Mohon hubungi administrator.](#ff7e5e)'
inventory_viewer_menu_title: '&0Inventaris milik %1%'
ender_chest_viewer_menu_title: '&0Peti Ender milik %1%'
inventory_viewer_opened: '[Melihat cuplikan inventaris milik](#00fb9a) [%1%](#00fb9a bold)[pada ⌚ %2%](#00fb9a)'
ender_chest_viewer_opened: '[Melihat cuplikan Peti Ender milik](#00fb9a) [%1%](#00fb9a bold)[pada ⌚ %2%](#00fb9a)'
data_update_complete: '[🔔 Datamu telah diperbarui!](#00fb9a)'
data_update_failed: '[🔔 Gagal memperbarui datamu! Mohon hubungi administrator.](#ff7e5e)'
user_registration_complete: '[⭐ Pendaftaran pengguna selesai!](#00fb9a)'
data_manager_title: '[Melihat cuplikan data pengguna](#00fb9a) [%1%](#00fb9a show_text=&7Versi UUID:\n&8%2%) [untuk](#00fb9a) [%3%](#00fb9a bold show_text=&7UUID pemain:\n&8%4%)[:](#00fb9a)'
data_manager_timestamp: '[⌚ %1%](#ffc43b-#f5c962 show_text=&7Versi stempel waktu:\n&8Ketika data disimpan)'
data_manager_pinned: '[※ Cuplikan disematkan](#d8ff2b show_text=&7Disematkan:\n&8Cuplikan data pengguna ini tidak akan diputar secara otomatis.)'
data_manager_cause: '[⚑ %1%](#23a825-#36f539 show_text=&7Penyebab penyimpanan:\n&8Apa yang menyebabkan data disimpan)'
data_manager_server: '[☁ %1%](#ff87b3-#f5538e show_text=&7Server:\n&8Nama server tempat data disimpan)'
data_manager_size: '[⏏ %1%](color=#62a9f5-#7ab8fa show_text=&7Ukuran cuplikan:\n&8Perkiraan ukuran file cuplikan (dalam KiB))\n'
data_manger_status: '[%1%](red)[/](gray)[%2%](red)[×](gray)[❤](red show_text=&7Poin kesehatan) [%3%](yellow)[×](gray)[🍖](yellow show_text=&7Poin kelaparan) [ʟᴠ](green)[.](gray)[%4%](green show_text=&7Level XP) [🏹 %5%](dark_aqua show_text=&7Mode game)'
data_manager_advancements_statistics: '[⭐ Kemajuan: %1%](color=#ffc43b-#f5c962 show_text=&7Kemajuan yang telah kamu capai:\n&8%2%) [⌛ Waktu Bermain: %3%ʜʀs](color=#62a9f5-#7ab8fa show_text=&7Waktu bermain dalam game\n&8⚠ Statistik berdasarkan dalam game)\n'
data_manager_item_buttons: '[Lihat:](gray) [[🪣 Inventaris…]](color=#a17b5f-#f5b98c show_text=&7Klik untuk lihat run_command=/inventory %1% %2%) [[⌀ Peti Ender…]](#b649c4-#d254ff show_text=&7Klik untuk lihat run_command=/enderchest %1% %2%)'
data_manager_management_buttons: '[Kelola:](gray) [[❌ Hapus…]](#ff3300 show_text=&7Klik untuk menghapus cuplikan data pengguna ini.\n&8Ini tidak akan berdampak pada data pengguna saat ini.\n&#ff3300&⚠ Hal ini tidak dapat dibatalkan! suggest_command=/husksync:userdata delete %1% %2%) [[⏪ Pulihkan…]](#00fb9a show_text=&7Klik untuk memulihkan data pengguna ini.\n&8Ini akan mengatur data pengguna ke cuplikan ini.\n&#ff3300&⚠ Data %1% saat ini akan ditimpa! suggest_command=/husksync:userdata restore %1% %2%) [[※ Sematkan/Tidak disematkan…]](#d8ff2b show_text=&7Klik untuk menyematkan atau tidak cuplikan data pengguna ini\n&8Cuplikan yang disematkan tidak akan diputar otomatis run_command=/userdata pin %1% %2%)'
data_manager_system_buttons: '[Sistem:](gray) [[⏷ Pembuangan File…]](dark_gray show_text=&7Klik untuk membuang cuplikan data mentah pengguna ini ke sebuah file.\n&8Data yang dibuang dapat ditemukan di ~/plugins/HuskSync/dumps/ run_command=/husksync:userdata dump %1% %2% file) [[☂ Pembuangan Web…]](dark_gray show_text=&7Klik untuk membuang cuplikan data mentah pengguna ini ke layanan mc-logs\n&8Kamu akan diberikan URL yang berisi data. run_command=/husksync:userdata dump %1% %2% web)'
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_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%)'
data_unpinned: '[※ Berhasil melepaskan cuplikan data pengguna yang disematkan](#00fb9a) [%1%](#00fb9a show_text=&7Versi UUID:\n&8%2%) [untuk](#00fb9a) [%3%.](#00fb9a show_text=&7UUID Pemain:\n&8%4%)'
data_dumped: '[☂ Berhasil membuang cuplikan data pengguna %1% untuk %2% ke:](#00fb9a) &7%3%'
list_footer: '\n%1%[Halaman](#00fb9a) [%2%](#00fb9a)/[%3%](#00fb9a)%4% %5%'
list_previous_page_button: '[◀](white show_text=&7Lihat halaman sebelumnya run_command=%2% %1%) '
list_next_page_button: ' [▶](white show_text=&7Lihat halaman selanjutnya run_command=%2% %1%)'
list_page_jumpers: '(%1%)'
list_page_jumper_button: '[%1%](show_text=&7Loncat ke halaman %1% run_command=%2% %1%)'
list_page_jumper_current_page: '[%1%](#00fb9a)'
list_page_jumper_separator: ' '
list_page_jumper_group_separator: '…'
save_cause_disconnect: 'memutuskan sambungan'
save_cause_world_save: 'penyimpanan dunia'
save_cause_death: 'kematian'
save_cause_server_shutdown: 'pematian server'
save_cause_inventory_command: 'perintah inventaris'
save_cause_enderchest_command: 'perintah enderchest'
save_cause_backup_restore: 'pemulihan cadangan'
save_cause_api: 'API'
save_cause_mpdb_migration: 'migrasi MPDB'
save_cause_legacy_migration: 'migrasi peninggalan'
save_cause_converted_from_v2: 'dikonversi dari v2'
up_to_date: '[HuskSync](#00fb9a bold) [| Kamu menjalankan versi terbaru dari HuskSync (v%1%).](#00fb9a)'
update_available: '[HuskSync](#ff7e5e bold) [| Versi baru HuskSync tersedia: v%1% (menjalankan: v%2%).](#ff7e5e)'
reload_complete: '[HuskSync](#00fb9a bold) [| Memuat ulang file konfigurasi dan pesan.](#00fb9a)\n[⚠ Pastikan file konfigurasi sudah diperbarui di semua server!](#00fb9a)\n[Diperlukan pengaktifan ulang agar perubahan konfigurasi dapat diterapkan.](#00fb9a italic)'
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_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.'
error_no_data_to_display: '[Kesalahan:](#ff3300) [Tidak dapat menemukan data pengguna untuk ditampilkan.](#ff7e5e)'
error_invalid_version_uuid: '[Kesalahan:](#ff3300) [Tidak dapat menemukan data pengguna untuk versi UUID itu.](#ff7e5e)'
husksync_command_description: 'Mengelola plugin HuskSync'
userdata_command_description: 'Lihat, kelola & pulihkan data pengguna pemain'
inventory_command_description: 'Lihat & edit inventaris pemain'
enderchest_command_description: 'Lihat & edit Ender Chest pemain'

View File

@@ -1,62 +1,63 @@
synchronization_complete: '[⏵ Dati sincronizzati!](#00fb9a)' locales:
synchronization_failed: '[⏵ Sincronizzazione fallita! Perfavore contatta un amministratore.](#ff7e5e)' synchronization_complete: '[⏵ Dati sincronizzati!](#00fb9a)'
inventory_viewer_menu_title: '&0Inventario di %1%' synchronization_failed: '[⏵ Sincronizzazione fallita! Perfavore contatta un amministratore.](#ff7e5e)'
ender_chest_viewer_menu_title: '&0Enderchest di %1%' inventory_viewer_menu_title: '&0Inventario di %1%'
inventory_viewer_opened: '[Stai vedendo l''istantanea di](#00fb9a) [%1%](#00fb9a bold) [inventario del ⌚ %2%](#00fb9a)' ender_chest_viewer_menu_title: '&0Enderchest di %1%'
ender_chest_viewer_opened: '[Stai vedendo l''istantanea di](#00fb9a) [%1%](#00fb9a bold) [Ender Chest del ⌚ %2%](#00fb9a)' inventory_viewer_opened: '[Stai vedendo l''istantanea di](#00fb9a) [%1%](#00fb9a bold) [inventario del ⌚ %2%](#00fb9a)'
data_update_complete: '[🔔 I tuoi dati sono stati aggiornati!](#00fb9a)' ender_chest_viewer_opened: '[Stai vedendo l''istantanea di](#00fb9a) [%1%](#00fb9a bold) [Ender Chest del ⌚ %2%](#00fb9a)'
data_update_failed: '[🔔 Aggiornamento dei tuoi dati fallito! Perfavore contatta un amministratore.](#ff7e5e)' data_update_complete: '[🔔 I tuoi dati sono stati aggiornati!](#00fb9a)'
user_registration_complete: '[⭐ User registration complete!](#00fb9a)' data_update_failed: '[🔔 Aggiornamento dei tuoi dati fallito! Perfavore contatta un amministratore.](#ff7e5e)'
data_manager_title: '[Stai vedendo l''istantanea](#00fb9a) [%1%](#00fb9a show_text=&7Versione di UUID:\n&8%2%) [di](#00fb9a) [%3%](#00fb9a bold show_text=&7Player UUID:\n&8%4%)[:](#00fb9a)' user_registration_complete: '[⭐ User registration complete!](#00fb9a)'
data_manager_timestamp: '[⌚ %1%](#ffc43b-#f5c962 show_text=&7:\n&8Quando i dati sono stati salvati)' data_manager_title: '[Stai vedendo l''istantanea](#00fb9a) [%1%](#00fb9a show_text=&7Versione di UUID:\n&8%2%) [di](#00fb9a) [%3%](#00fb9a bold show_text=&7Player UUID:\n&8%4%)[:](#00fb9a)'
data_manager_pinned: '[※ Istantanea fissata](#d8ff2b show_text=&7Pinned:\n&8Quest''istantanea non sarà cancellata automaticamente.)' data_manager_timestamp: '[⌚ %1%](#ffc43b-#f5c962 show_text=&7:\n&8Quando i dati sono stati salvati)'
data_manager_cause: '[⚑ %1%](#23a825-#36f539 show_text=&7Causa di salvataggio:\n&8Cosa ha causato il salvataggio dei dati)' data_manager_pinned: '[※ Istantanea fissata](#d8ff2b show_text=&7Pinned:\n&8Quest''istantanea non sarà cancellata automaticamente.)'
data_manager_server: '[ %1%](#ff87b3-#f5538e show_text=&7Server:\n&8Name of the server the data was saved on)' data_manager_cause: '[ %1%](#23a825-#36f539 show_text=&7Causa di salvataggio:\n&8Cosa ha causato il salvataggio dei dati)'
data_manager_size: '[ %1%](color=#62a9f5-#7ab8fa show_text=&7Peso dell''istantanea:\n&8Peso stimato del file (in KiB))\n' data_manager_server: '[ %1%](#ff87b3-#f5538e show_text=&7Server:\n&8Name of the server the data was saved on)'
data_manger_status: '[%1%](red)[/](gray)[%2%](red)[×](gray)[❤](red show_text=&7Vita) [%3%](yellow)[×](gray)[🍖](yellow show_text=&7Fame) [ʟᴠ](green)[.](gray)[%4%](green show_text=&7Livello di XP) [🏹 %5%](dark_aqua show_text=&7Modalità di gioco)' data_manager_size: '[%1%](color=#62a9f5-#7ab8fa show_text=&7Peso dell''istantanea:\n&8Peso stimato del file (in KiB))\n'
data_manager_advancements_statistics: '[⭐ Progressi: %1%](color=#ffc43b-#f5c962 show_text=&7Progressi compiuti in:\n&8%2%) [⌛ Tempo di gioco: %3%ʜʀs](color=#62a9f5-#7ab8fa show_text=&7Tempo di gioco\n&8⚠ Basato sulle statistiche di gioco)\n' data_manger_status: '[%1%](red)[/](gray)[%2%](red)[×](gray)[❤](red show_text=&7Vita) [%3%](yellow)[×](gray)[🍖](yellow show_text=&7Fame) [ʟᴠ](green)[.](gray)[%4%](green show_text=&7Livello di XP) [🏹 %5%](dark_aqua show_text=&7Modalità di gioco)'
data_manager_item_buttons: '[View:](gray) [[🪣 Inventario…]](color=#a17b5f-#f5b98c show_text=&7Clicca per visualizzare run_command=/inventory %1% %2%) [[⌀ Ender Chest…]](#b649c4-#d254ff show_text=&7Clicca per visualizzare run_command=/enderchest %1% %2%)' data_manager_advancements_statistics: '[⭐ Progressi: %1%](color=#ffc43b-#f5c962 show_text=&7Progressi compiuti in:\n&8%2%) [⌛ Tempo di gioco: %3%ʜʀs](color=#62a9f5-#7ab8fa show_text=&7Tempo di gioco\n&8⚠ Basato sulle statistiche di gioco)\n'
data_manager_management_buttons: '[Gestisci:](gray) [[❌ Cancella…]](#ff3300 show_text=&7Fare clic per eliminare questa istantanea.\n&8Questo non influisce sui dati attuali dell''utente.\n&#ff3300&⚠ Questo non può essere annullato! suggest_command=/husksync:userdata delete %1% %2%) [[⏪ Ripristina…]](#00fb9a show_text=&7Clicca per ripristinare i dati dell''utente.\n&8I dati dell''utente saranno ripristinati a quest''istantanea.\n&#ff3300&⚠ I dati di %1% saranno sovrascritti! suggest_command=/husksync:userdata restore %1% %2%) [[※ fissa/sblocca...]](#d8ff2b show_text=&7Clicca per fissare o sbloccare quest''istantanea\n&8Le istantanee fissate non saranno cancellate automaticamente run_command=/userdata pin %1% %2%)' data_manager_item_buttons: '[View:](gray) [[🪣 Inventario…]](color=#a17b5f-#f5b98c show_text=&7Clicca per visualizzare run_command=/inventory %1% %2%) [[⌀ Ender Chest…]](#b649c4-#d254ff show_text=&7Clicca per visualizzare run_command=/enderchest %1% %2%)'
data_manager_system_buttons: '[Sistema:](gray) [[⏷ Dump del File…]](dark_gray show_text=&7Clicca per ottenere il dump dei dati del giocatore.\n&8I dati salvati sono posizioanti nella cartella ~/plugins/HuskSync/dumps/ run_command=/husksync:userdata dump %1% %2% file) [[☂ Dump su Web…]](dark_gray show_text=&7Clicca per ottenere il dump del file su mc-logs\n&8 Ti verrà consegnato l''url per visionare il dump. run_command=/husksync:userdata dump %1% %2% web)' data_manager_management_buttons: '[Gestisci:](gray) [[❌ Cancella…]](#ff3300 show_text=&7Fare clic per eliminare questa istantanea.\n&8Questo non influisce sui dati attuali dell''utente.\n&#ff3300&⚠ Questo non può essere annullato! suggest_command=/husksync:userdata delete %1% %2%) [[⏪ Ripristina…]](#00fb9a show_text=&7Clicca per ripristinare i dati dell''utente.\n&8I dati dell''utente saranno ripristinati a quest''istantanea.\n&#ff3300&⚠ I dati di %1% saranno sovrascritti! suggest_command=/husksync:userdata restore %1% %2%) [[※ fissa/sblocca...]](#d8ff2b show_text=&7Clicca per fissare o sbloccare quest''istantanea\n&8Le istantanee fissate non saranno cancellate automaticamente run_command=/userdata pin %1% %2%)'
data_manager_advancements_preview_remaining: 'e %1% altro…' data_manager_system_buttons: '[Sistema:](gray) [[⏷ Dump del File…]](dark_gray show_text=&7Clicca per ottenere il dump dei dati del giocatore.\n&8I dati salvati sono posizioanti nella cartella ~/plugins/HuskSync/dumps/ run_command=/husksync:userdata dump %1% %2% file) [[☂ Dump su Web…]](dark_gray show_text=&7Clicca per ottenere il dump del file su mc-logs\n&8 Ti verrà consegnato l''url per visionare il dump. run_command=/husksync:userdata dump %1% %2% web)'
data_list_title: '[Lista delle istantanee di %1%:](#00fb9a) [(%2%-%3% of](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n' data_manager_advancements_preview_remaining: 'e %1% altro…'
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_title: '[Lista delle istantanee di %1%:](#00fb9a) [(%2%-%3% of](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n'
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_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_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_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_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%)' 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_unpinned: '[※ L''istantanea dei dati utente è stata sbloccata 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_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%)'
data_dumped: '[☂ Hai ottenuto il dump dell''istantanea %1% di %2% nel formato:](#00fb9a) &7%3%' data_unpinned: '[※ L''istantanea dei dati utente è stata sbloccata con successo](#00fb9a) [%1%](#00fb9a show_text=&7Versione di UUID:\n&8%2%) [per](#00fb9a) [%3%.](#00fb9a show_text=&7Player UUID:\n&8%4%)'
list_footer: '\n%1%[Pagina](#00fb9a) [%2%](#00fb9a)/[%3%](#00fb9a)%4% %5%' data_dumped: '[☂ Hai ottenuto il dump dell''istantanea %1% di %2% nel formato:](#00fb9a) &7%3%'
list_previous_page_button: '[◀](white show_text=&7Visualizza pagina precedente run_command=%2% %1%) ' list_footer: '\n%1%[Pagina](#00fb9a) [%2%](#00fb9a)/[%3%](#00fb9a)%4% %5%'
list_next_page_button: ' [▶](white show_text=&7Visualizza pagina successiva run_command=%2% %1%)' list_previous_page_button: '[◀](white show_text=&7Visualizza pagina precedente run_command=%2% %1%) '
list_page_jumpers: '(%1%)' list_next_page_button: ' [▶](white show_text=&7Visualizza pagina successiva run_command=%2% %1%)'
list_page_jumper_button: '[%1%](show_text=&7Vai alla pagina %1% run_command=%2% %1%)' list_page_jumpers: '(%1%)'
list_page_jumper_current_page: '[%1%](#00fb9a)' list_page_jumper_button: '[%1%](show_text=&7Vai alla pagina %1% run_command=%2% %1%)'
list_page_jumper_separator: ' ' list_page_jumper_current_page: '[%1%](#00fb9a)'
list_page_jumper_group_separator: '' list_page_jumper_separator: ' '
save_cause_disconnect: 'disconnect' list_page_jumper_group_separator: '…'
save_cause_world_save: 'world save' save_cause_disconnect: 'disconnect'
save_cause_death: 'death' save_cause_world_save: 'world save'
save_cause_server_shutdown: 'server shutdown' save_cause_death: 'death'
save_cause_inventory_command: 'inventory command' save_cause_server_shutdown: 'server shutdown'
save_cause_enderchest_command: 'enderchest command' save_cause_inventory_command: 'inventory command'
save_cause_backup_restore: 'backup restore' save_cause_enderchest_command: 'enderchest command'
save_cause_api: 'API' save_cause_backup_restore: 'backup restore'
save_cause_mpdb_migration: 'MPDB migration' save_cause_api: 'API'
save_cause_legacy_migration: 'legacy migration' save_cause_mpdb_migration: 'MPDB migration'
save_cause_converted_from_v2: 'converted from v2' save_cause_legacy_migration: 'legacy migration'
up_to_date: '[HuskSync](#00fb9a bold) [| Il plugin è all''ultima versione disponibile (v%1%).](#00fb9a)' save_cause_converted_from_v2: 'converted from v2'
update_available: '[HuskSync](#ff7e5e bold) [| Disponibile una nuova versione: v%1% (running: v%2%).](#ff7e5e)' up_to_date: '[HuskSync](#00fb9a bold) [| Il plugin è all''ultima versione disponibile (v%1%).](#00fb9a)'
reload_complete: '[HuskSync](#00fb9a bold) [| Configurazione e messaggi ricaricati.](#00fb9a)\n[⚠ Ensure config files are up-to-date on all servers!](#00fb9a)\n[A restart is needed for config changes to take effect.](#00fb9a italic)' update_available: '[HuskSync](#ff7e5e bold) [| Disponibile una nuova versione: v%1% (running: v%2%).](#ff7e5e)'
system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)' reload_complete: '[HuskSync](#00fb9a bold) [| Configurazione e messaggi ricaricati.](#00fb9a)\n[⚠ Ensure config files are up-to-date on all servers!](#00fb9a)\n[A restart is needed for config changes to take effect.](#00fb9a italic)'
error_invalid_syntax: '[Errore:](#ff3300) [Sintassi errata. Usa:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)' system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)'
error_invalid_player: '[Errore:](#ff3300) [Impossibile trovare un giocatore con questo nome.](#ff7e5e)' error_invalid_syntax: '[Errore:](#ff3300) [Sintassi errata. Usa:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)'
error_no_permission: '[Errore:](#ff3300) [Non hai il permesso di usare questo comando](#ff7e5e)' error_invalid_player: '[Errore:](#ff3300) [Impossibile trovare un giocatore con questo nome.](#ff7e5e)'
error_console_command_only: '[Errore:](#ff3300) [Questo comando può essere eseguito solo dalla](#ff7e5e)' error_no_permission: '[Errore:](#ff3300) [Non hai il permesso di usare questo comando](#ff7e5e)'
error_in_game_command_only: 'Errore: Questo comando può essere utilizzato solo in gioco.' error_console_command_only: '[Errore:](#ff3300) [Questo comando può essere eseguito solo dalla](#ff7e5e)'
error_no_data_to_display: '[Errore:](#ff3300) [Impossibile trovare dati da visualizzare.](#ff7e5e)' error_in_game_command_only: 'Errore: Questo comando può essere utilizzato solo in gioco.'
error_invalid_version_uuid: '[Errore:](#ff3300) [Impossibile trovare dati utente per questa versione di UUID.](#ff7e5e)' error_no_data_to_display: '[Errore:](#ff3300) [Impossibile trovare dati da visualizzare.](#ff7e5e)'
husksync_command_description: 'Gestisci il plugin HuskSync' error_invalid_version_uuid: '[Errore:](#ff3300) [Impossibile trovare dati utente per questa versione di UUID.](#ff7e5e)'
userdata_command_description: 'Vedi, gestisci e recupera i dati del giocatore' husksync_command_description: 'Gestisci il plugin HuskSync'
inventory_command_description: 'Vedi e modifica l''Inventario di un giocatore' userdata_command_description: 'Vedi, gestisci e recupera i dati del giocatore'
enderchest_command_description: 'Vedi e modifica l''Ender Chest di un giocatore' inventory_command_description: 'Vedi e modifica l''Inventario di un giocatore'
enderchest_command_description: 'Vedi e modifica l''Ender Chest di un giocatore'

View File

@@ -1,62 +1,63 @@
synchronization_complete: '[⏵データが同期されました!](#00fb9a)' locales:
synchronization_failed: '[⏵ データ同期に失敗しました!管理者に連絡してください。](#ff7e5e)' synchronization_complete: '[⏵データ同期されました!](#00fb9a)'
inventory_viewer_menu_title: '&0%1%のインベントリ' synchronization_failed: '[⏵ データの同期に失敗しました!管理者に連絡してください。](#ff7e5e)'
ender_chest_viewer_menu_title: '&0%1%のエンダーチェスト' inventory_viewer_menu_title: '&0%1%のインベントリ'
inventory_viewer_opened: '[⌚ %2%](#00fb9a) [%1%](#00fb9a bold) [のインベントリのスナップショットを閲覧する](#00fb9a)' ender_chest_viewer_menu_title: '&0%1%のエンダーチェスト'
ender_chest_viewer_opened: '[⌚ %2%](#00fb9a) [%1%](#00fb9a bold) [のエンダーチェストのスナップショットを閲覧する](#00fb9a)' inventory_viewer_opened: '[⌚ %2%](#00fb9a) [%1%](#00fb9a bold) [のインベントリのスナップショットを閲覧する](#00fb9a)'
data_update_complete: '[🔔 データが更新されました!](#00fb9a)' ender_chest_viewer_opened: '[⌚ %2%](#00fb9a) [%1%](#00fb9a bold) [のエンダーチェストのスナップショットを閲覧する](#00fb9a)'
data_update_failed: '[🔔 データ更新に失敗しました!管理者に連絡してください。](#ff7e5e)' data_update_complete: '[🔔 データ更新されました!](#00fb9a)'
user_registration_complete: '[⭐ ユーザー登録が完了しました!](#00fb9a)' data_update_failed: '[🔔 データの更新に失敗しました!管理者に連絡してください。](#ff7e5e)'
data_manager_title: '[%3%](#00fb9a bold show_text=&7プレイヤーUUID:\n&8%4%) [のユーザーデータスナップショット](#00fb9a)[%1%](#00fb9a show_text=&7バージョンUUID:\n&8%2%)[を表示:](#00fb9a)' user_registration_complete: '[⭐ ユーザー登録が完了しました!](#00fb9a)'
data_manager_timestamp: '[⌚ %1%](#ffc43b-#f5c962 show_text=&7バージョンタイムスタンプ:\n&8データの保存時期)' data_manager_title: '[%3%](#00fb9a bold show_text=&7プレイヤーUUID:\n&8%4%) [のユーザーデータスナップショット](#00fb9a)[%1%](#00fb9a show_text=&7バージョンUUID:\n&8%2%)[を表示:](#00fb9a)'
data_manager_pinned: '[※ ピン留めされたスナップショット](#d8ff2b show_text=&7ピン留め:\n&8このユーザーデータのスナップショットは自動的にローテーションされません。)' data_manager_timestamp: '[⌚ %1%](#ffc43b-#f5c962 show_text=&7バージョンタイムスタンプ:\n&8データの保存時期)'
data_manager_cause: '[⚑ %1%](#23a825-#36f539 show_text=&7保存理由:\n&8データが保存された理由)' data_manager_pinned: '[※ ピン留めされたスナップショット](#d8ff2b show_text=&7ピン留め:\n&8このユーザーデータのスナップショットは自動的にローテーションされません。)'
data_manager_server: '[ %1%](#ff87b3-#f5538e show_text=&7Server:\n&8Name of the server the data was saved on)' data_manager_cause: '[ %1%](#23a825-#36f539 show_text=&7保存理由:\n&8データが保存された理由)'
data_manager_size: '[ %1%](color=#62a9f5-#7ab8fa show_text=&7スナップショットサイズ:\n&8スナップショットの推定ファイルサイズ単位:KiB)\n' data_manager_server: '[ %1%](#ff87b3-#f5538e show_text=&7Server:\n&8Name of the server the data was saved on)'
data_manger_status: '[%1%](red)[/](gray)[%2%](red)[×](gray)[❤](red show_text=&7体力) [%3%](yellow)[×](gray)[🍖](yellow show_text=&7空腹度) [ʟᴠ](green)[.](gray)[%4%](green show_text=&7経験値レベル) [🏹 %5%](dark_aqua show_text=&7ゲームモード)' data_manager_size: '[%1%](color=#62a9f5-#7ab8fa show_text=&7スナップショットサイズ:\n&8スナップショットの推定ファイルサイズ単位:KiB)\n'
data_manager_advancements_statistics: '[⭐ 進捗: %1%](color=#ffc43b-#f5c962 show_text=&7達成した進捗:\n&8%2%) [⌛ プレイ時間: %3%ʜʀs](color=#62a9f5-#7ab8fa show_text=&7ゲーム内のプレイ時間\n&8⚠ ゲーム内の統計に基づく)\n' data_manger_status: '[%1%](red)[/](gray)[%2%](red)[×](gray)[❤](red show_text=&7体力) [%3%](yellow)[×](gray)[🍖](yellow show_text=&7空腹度) [ʟᴠ](green)[.](gray)[%4%](green show_text=&7経験値レベル) [🏹 %5%](dark_aqua show_text=&7ゲームモード)'
data_manager_item_buttons: '[表示:](gray) [[🪣 インベントリ…]](color=#a17b5f-#f5b98c show_text=&7クリックで表示 run_command=/husksync:inventory %1% %2%) [[⌀ エンダーチェスト…]](#b649c4-#d254ff show_text=&7クリックで表示 run_command=/husksync:enderchest %1% %2%)' data_manager_advancements_statistics: '[⭐ 進捗: %1%](color=#ffc43b-#f5c962 show_text=&7達成した進捗:\n&8%2%) [⌛ プレイ時間: %3%ʜʀs](color=#62a9f5-#7ab8fa show_text=&7ゲーム内のプレイ時間\n&8⚠ ゲーム内の統計に基づく)\n'
data_manager_management_buttons: '[管理:](gray) [[❌ 消去…]](#ff3300 show_text=&7クリックでこのユーザーデータのスナップショットを消去します。\n&8これはユーザーの現在のデータには影響しません。\n&#ff3300&⚠ この操作は元に戻せません! suggest_command=/husksync:userdata delete %1% %2%) [[⏪ 復元…]](#00fb9a show_text=&7クリックでこのユーザーデータを復元します。\n&8これにより、ユーザーデータはこのスナップショットに設定されます。\n&#ff3300&⚠ %1% の現在のデータは上書きされます! suggest_command=/husksync:userdata restore %1% %2%) [[※ ピン留め/ピン外し…]](#d8ff2b show_text=&7クリックでこのユーザーデータのスナップショットをピン留め、若しくはピンを外します。\n&8ピン留めされたスナップショットは自動的にローテーションしません。 run_command=/userdata pin %1% %2%)' data_manager_item_buttons: '[表示:](gray) [[🪣 インベントリ…]](color=#a17b5f-#f5b98c show_text=&7クリックで表示 run_command=/husksync:inventory %1% %2%) [[⌀ エンダーチェスト…]](#b649c4-#d254ff show_text=&7クリックで表示 run_command=/husksync:enderchest %1% %2%)'
data_manager_system_buttons: '[システム:](gray) [[⏷ ファイルダンプ…]](dark_gray show_text=&7クリックで未加工のユーザーデータスナップショットをダンプファイルにします。\n&8データダンプの場所は ~/plugins/HuskSync/dumps/ です run_command=/husksync:userdata dump %1% %2% file) [[☂ Webダンプ…]](dark_gray show_text=&7クリックでユーザーデータスナップショットをmc-logsサービスにダンプします。\n&8データを含むURLが提供されます。 run_command=/husksync:userdata dump %1% %2% web)' data_manager_management_buttons: '[管理:](gray) [[❌ 消去…]](#ff3300 show_text=&7クリックでのユーザーデータスナップショットを消去します。\n&8これはユーザーの現在のデータには影響しません。\n&#ff3300&⚠ この操作は元に戻せません! suggest_command=/husksync:userdata delete %1% %2%) [[⏪ 復元…]](#00fb9a show_text=&7クリックでこのユーザーデータを復元します。\n&8これにより、ユーザーデータはこのスナップショットに設定されます。\n&#ff3300&⚠ %1% の現在のデータは上書きされます! suggest_command=/husksync:userdata restore %1% %2%) [[※ ピン留め/ピン外し…]](#d8ff2b show_text=&7クリックでこのユーザーデータスナップショットをピン留め、若しくはピンを外します。\n&8ピン留めされたスナップショットは自動的にローテーションしません。 run_command=/userdata pin %1% %2%)'
data_manager_advancements_preview_remaining: 'さらに %1% 件…' data_manager_system_buttons: '[システム:](gray) [[⏷ ファイルダンプ…]](dark_gray show_text=&7クリックで未加工のユーザーデータスナップショットをダンプファイルにします。\n&8データダンプの場所は ~/plugins/HuskSync/dumps/ です run_command=/husksync:userdata dump %1% %2% file) [[☂ Webダンプ…]](dark_gray show_text=&7クリックでユーザーデータスナップショットをmc-logsサービスにダンプします。\n&8データを含むURLが提供されます。 run_command=/husksync:userdata dump %1% %2% web)'
data_list_title: '[%1% のユーザーデータスナップショット:](#00fb9a) [(%4%件中](#00fb9a bold) [%2%-%3%件](#00fb9a)[)](#00fb9a)\n' data_manager_advancements_preview_remaining: 'さらに %1% 件…'
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_title: '[%1% のユーザーデータスナップショット:](#00fb9a) [(%4%件中](#00fb9a bold) [%2%-%3%件](#00fb9a)[)](#00fb9a)\n'
data_deleted: '[❌](#00fb9a) [%3%](#00fb9a show_text=&7Player UUID:\n&8%4%) [のユーザーデータスナップショット](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\n&8%2%) [の消去に成功しました。](#00fb9a)' 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_restored: '[⏪](#00fb9a) [スナップショット](#00fb9a) [%3%](#00fb9a show_text=&7Version UUID:\n&8%4%) [から](#00fb9a) [%1%](#00fb9a show_text=&7Player UUID:\n&8%2%) [の現在のユーザーデータの復元に成功しました。](#00fb9a)' data_deleted: '[](#00fb9a) [%3%](#00fb9a show_text=&7Player UUID:\n&8%4%) [のユーザーデータスナップショット](#00fb9a) [%1%](#00fb9a show_text=&7Version 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)' 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_unpinned: '[※](#00fb9a) [%3%](#00fb9a show_text=&7Player UUID:\n&8%4%) [のユーザーデータスナップショット](#00fb9a) [%1%](#00fb9a show_text=&7Version 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)'
data_dumped: '[☂ %2% のユーザーデータスナップショット %1% のダンプに成功:](#00fb9a) &7%3%' data_unpinned: '[※](#00fb9a) [%3%](#00fb9a show_text=&7Player UUID:\n&8%4%) [のユーザーデータスナップショット](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\n&8%2%) [のピン外しに成功しました。](#00fb9a)'
list_footer: '\n%1%[ページ](#00fb9a) [%2%](#00fb9a)/[%3%](#00fb9a)%4% %5%' data_dumped: '[☂ %2% のユーザーデータスナップショット %1% のダンプに成功:](#00fb9a) &7%3%'
list_previous_page_button: '[◀](white show_text=&7前のページへ run_command=%2% %1%) ' list_footer: '\n%1%[ページ](#00fb9a) [%2%](#00fb9a)/[%3%](#00fb9a)%4% %5%'
list_next_page_button: ' [▶](white show_text=&7のページへ run_command=%2% %1%)' list_previous_page_button: '[◀](white show_text=&7のページへ run_command=%2% %1%) '
list_page_jumpers: '(%1%)' list_next_page_button: ' [▶](white show_text=&7次のページへ run_command=%2% %1%)'
list_page_jumper_button: '[%1%](show_text=&7%1% ページ目へ run_command=%2% %1%)' list_page_jumpers: '(%1%)'
list_page_jumper_current_page: '[%1%](#00fb9a)' list_page_jumper_button: '[%1%](show_text=&7%1% ページ目へ run_command=%2% %1%)'
list_page_jumper_separator: ' ' list_page_jumper_current_page: '[%1%](#00fb9a)'
list_page_jumper_group_separator: '' list_page_jumper_separator: ' '
save_cause_disconnect: 'disconnect' list_page_jumper_group_separator: '…'
save_cause_world_save: 'world save' save_cause_disconnect: 'disconnect'
save_cause_death: 'death' save_cause_world_save: 'world save'
save_cause_server_shutdown: 'server shutdown' save_cause_death: 'death'
save_cause_inventory_command: 'inventory command' save_cause_server_shutdown: 'server shutdown'
save_cause_enderchest_command: 'enderchest command' save_cause_inventory_command: 'inventory command'
save_cause_backup_restore: 'backup restore' save_cause_enderchest_command: 'enderchest command'
save_cause_api: 'API' save_cause_backup_restore: 'backup restore'
save_cause_mpdb_migration: 'MPDB migration' save_cause_api: 'API'
save_cause_legacy_migration: 'legacy migration' save_cause_mpdb_migration: 'MPDB migration'
save_cause_converted_from_v2: 'converted from v2' save_cause_legacy_migration: 'legacy migration'
up_to_date: '[HuskSync](#00fb9a bold) [| HuskSyncの最新バージョンを実行しています(v%1%).](#00fb9a)' save_cause_converted_from_v2: 'converted from v2'
update_available: '[HuskSync](#ff7e5e bold) [| HuskSyncの最新バージョンが更新されています: v%1% (実行中: v%2%).](#ff7e5e)' up_to_date: '[HuskSync](#00fb9a bold) [| HuskSyncの最新バージョンを実行しています(v%1%).](#00fb9a)'
reload_complete: '[HuskSync](#00fb9a bold) [| 設定ファイルとメッセージファイルを再読み込みしました。](#00fb9a)\n[⚠ すべてのサーバーで設定ファイルが最新であることを確認してください!](#00fb9a)\n[設定の変更を有効にするには再起動が必要です。](#00fb9a italic)' update_available: '[HuskSync](#ff7e5e bold) [| HuskSyncの最新バージョンが更新されています: v%1% (実行中: v%2%).](#ff7e5e)'
system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)' reload_complete: '[HuskSync](#00fb9a bold) [| 設定ファイルとメッセージファイルを再読み込みしました。](#00fb9a)\n[⚠ すべてのサーバーで設定ファイルが最新であることを確認してください!](#00fb9a)\n[設定の変更を有効にするには再起動が必要です。](#00fb9a italic)'
error_invalid_syntax: '[Error:](#ff3300) [構文が正しくありません。使用法:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&クリックでサジェスト suggest_command=%1%)' system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)'
error_invalid_player: '[Error:](#ff3300) [そのプレイヤーは見つかりませんでした](#ff7e5e)' error_invalid_syntax: '[Error:](#ff3300) [構文が正しくありません。使用法:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&クリックでサジェスト suggest_command=%1%)'
error_no_permission: '[Error:](#ff3300) [このコマンドを実行する権限がありません](#ff7e5e)' error_invalid_player: '[Error:](#ff3300) [そのプレイヤーは見つかりませんでした](#ff7e5e)'
error_console_command_only: '[Error:](#ff3300) [のコマンドは%1%コンソールからのみ実行できます](#ff7e5e)' error_no_permission: '[Error:](#ff3300) [のコマンドを実行する権限がありません](#ff7e5e)'
error_in_game_command_only: 'Error: そのコマンドはゲーム内でしか使えません。' error_console_command_only: '[Error:](#ff3300) [そのコマンドは%1%コンソールからのみ実行できます](#ff7e5e)'
error_no_data_to_display: '[Error:](#ff3300) [表示するユーザーデータが見つかりませんでした。](#ff7e5e)' error_in_game_command_only: 'Error: そのコマンドはゲーム内でしか使えません。'
error_invalid_version_uuid: '[Error:](#ff3300) [そのバージョンUUIDのユーザーデータが見つかりませんでした。](#ff7e5e)' error_no_data_to_display: '[Error:](#ff3300) [表示するユーザーデータが見つかりませんでした。](#ff7e5e)'
husksync_command_description: 'HuskSyncプラグインを管理する' error_invalid_version_uuid: '[Error:](#ff3300) [そのバージョンUUIDのユーザーデータが見つかりませんでした。](#ff7e5e)'
userdata_command_description: 'プレーヤーのユーザーデータを表示・管理・復元する' husksync_command_description: 'HuskSyncプラグインを管理する'
inventory_command_description: 'プレヤーのインベントリを閲覧・編集する' userdata_command_description: 'プレヤーのユーザーデータを表示・管理・復元する'
enderchest_command_description: 'プレイヤーのエンダーチェストを閲覧・編集する' inventory_command_description: 'プレイヤーのインベントリを閲覧・編集する'
enderchest_command_description: 'プレイヤーのエンダーチェストを閲覧・編集する'

View File

@@ -1,62 +1,63 @@
synchronization_complete: '[⏵ 데이터 연동됨!](#00fb9a)' locales:
synchronization_failed: '[⏵ 데이터 연동에 실패하였습니다! 관리자에게 문의해 주세요.](#ff7e5e)' synchronization_complete: '[⏵ 데이터 연동됨!](#00fb9a)'
inventory_viewer_menu_title: '&0%1%님의 인벤토리' synchronization_failed: '[⏵ 데이터 연동에 실패하였습니다! 관리자에게 문의해 주세요.](#ff7e5e)'
ender_chest_viewer_menu_title: '&0%1%님의 엔더상자' inventory_viewer_menu_title: '&0%1%님의 인벤토리'
inventory_viewer_opened: '[%1%](#00fb9a bold)[님의 ⌚ %2%의 인벤토리를 엽니다](#00fb9a)' ender_chest_viewer_menu_title: '&0%1%님의 엔더상자'
ender_chest_viewer_opened: '[%1%](#00fb9a bold)[님의 ⌚ %2%의 엔더상자를 엽니다](#00fb9a)' inventory_viewer_opened: '[%1%](#00fb9a bold)[님의 ⌚ %2%의 인벤토리를 엽니다](#00fb9a)'
data_update_complete: '[🔔 당신의 데이터가 업데이트 되었습니다!](#00fb9a)' ender_chest_viewer_opened: '[%1%](#00fb9a bold)[님의 ⌚ %2%의 엔더상자를 엽니다](#00fb9a)'
data_update_failed: '[🔔 데이터 업데이트에 실패하였습니다! 관리자에게 문의해 주세요.](#ff7e5e)' data_update_complete: '[🔔 당신의 데이터 업데이트 되었습니다!](#00fb9a)'
user_registration_complete: '[⭐ 유저 등록이 완료되었습니다!](#00fb9a)' data_update_failed: '[🔔 데이터 업데이트에 실패하였습니다! 관리자에게 문의해 주세요.](#ff7e5e)'
data_manager_title: '[%3%](#00fb9a bold show_text=&7플레이어 UUID:\n&8%4%)[님의 ](#00fb9a) [%1%](#00fb9a show_text=&7버전 UUID:\n&8%2%) [데이터 스냅샷을 표시합니다](#00fb9a)[:](#00fb9a)' user_registration_complete: '[⭐ 유저 등록이 완료되었습니다!](#00fb9a)'
data_manager_timestamp: '[⌚ %1%](#ffc43b-#f5c962 show_text=&7저장 시각:\n&8데이터가 저장된 시각)' data_manager_title: '[%3%](#00fb9a bold show_text=&7플레이어 UUID:\n&8%4%)[님의 ](#00fb9a) [%1%](#00fb9a show_text=&7버전 UUID:\n&8%2%) [데이터 스냅샷을 표시합니다](#00fb9a)[:](#00fb9a)'
data_manager_pinned: '[※ 스냅샷 고정됨](#d8ff2b show_text=&7고정됨:\n&8이 유저의 데이터 스냅샷은 자동으로 갱신되지 않습니다.)' data_manager_timestamp: '[⌚ %1%](#ffc43b-#f5c962 show_text=&7저장 시각:\n&8데이터가 저장된 시각)'
data_manager_cause: '[⚑ %1%](#23a825-#36f539 show_text=&7저장 사유:\n&8데이터가 저장된 사유입니다.)' data_manager_pinned: '[※ 스냅샷 고정됨](#d8ff2b show_text=&7고정됨:\n&8이 유저의 데이터 스냅샷은 자동으로 갱신되지 않습니다.)'
data_manager_server: '[ %1%](#ff87b3-#f5538e show_text=&7서버:\n&8데이터 저장이 이루어진 서버입니다.)' data_manager_cause: '[ %1%](#23a825-#36f539 show_text=&7저장 사유:\n&8데이터 저장된 사유입니다.)'
data_manager_size: '[ %1%](color=#62a9f5-#7ab8fa show_text=&7스냅샷 크기:\n&8스냅샷 파일의 대략적인 크기입니다. (단위 KiB))\n' data_manager_server: '[ %1%](#ff87b3-#f5538e show_text=&7서버:\n&8데이터 저장이 이루어진 서버입니다.)'
data_manger_status: '[%1%](red)[/](gray)[%2%](red)[×](gray)[❤](red show_text=&7체력) [%3%](yellow)[×](gray)[🍖](yellow show_text=&7허기) [ʟᴠ](green)[.](gray)[%4%](green show_text=&7경험치 레벨) [🏹 %5%](dark_aqua show_text=&7게임 모드)' data_manager_size: '[%1%](color=#62a9f5-#7ab8fa show_text=&7스냅샷 크기:\n&8스냅샷 파일의 대략적인 크기입니다. (단위 KiB))\n'
data_manager_advancements_statistics: '[⭐ 도전 과제: %1%](color=#ffc43b-#f5c962 show_text=&7진행한 도전 과제:\n&8%2%) [⌛ 플레이 타임: %3%ʜʀs](color=#62a9f5-#7ab8fa show_text=&7게임 플레이 시간\n&8⚠ 인게임 통계에 기반합니다.)\n' data_manger_status: '[%1%](red)[/](gray)[%2%](red)[×](gray)[❤](red show_text=&7체력) [%3%](yellow)[×](gray)[🍖](yellow show_text=&7허기) [ʟᴠ](green)[.](gray)[%4%](green show_text=&7경험치 레벨) [🏹 %5%](dark_aqua show_text=&7게임 모드)'
data_manager_item_buttons: '[보기:](gray) [[🪣 인벤토리…]](color=#a17b5f-#f5b98c show_text=&7클릭하여 확인 run_command=/inventory %1% %2%) [[⌀ 엔더상자…]](#b649c4-#d254ff show_text=&7클릭하여 확인 run_command=/enderchest %1% %2%)' data_manager_advancements_statistics: '[⭐ 도전 과제: %1%](color=#ffc43b-#f5c962 show_text=&7진행한 도전 과제:\n&8%2%) [⌛ 플레이 타임: %3%ʜʀs](color=#62a9f5-#7ab8fa show_text=&7인게임 플레이 시간\n&8⚠ 인게임 통계에 기반합니다.)\n'
data_manager_management_buttons: '[관리:](gray) [[❌ 삭제…]](#ff3300 show_text=&7클릭하여 이 유저 스냅샷 데이터를 삭제\n&8이 기능은 현재 유저의 인벤토리 데이터에는 영향을 미치지 않습니다.\n&#ff3300&⚠ 이 작업은 되돌릴 수 없습니다! suggest_command=/husksync:userdata delete %1% %2%) [[⏪ 복구…]](#00fb9a show_text=&7클릭하여 유저 데이터 복구\n&8유저의 데이터가 이 스냅샷의 데이터로 변경됩니다.\n&#ff3300&⚠ %1%님의 현재 데이터에 덧씌워 집니다! suggest_command=/husksync:userdata restore %1% %2%) [[※ 고정/고정 해제…]](#d8ff2b show_text=&7클릭하여 유저 데이터를 고정 또는 고정 해제\n&8고정된 스냅샷은 자동적으로 갱신되지 않습니다. run_command=/userdata pin %1% %2%)' data_manager_item_buttons: '[보기:](gray) [[🪣 인벤토리…]](color=#a17b5f-#f5b98c show_text=&7클릭하여 확인 run_command=/inventory %1% %2%) [[⌀ 엔더상자…]](#b649c4-#d254ff show_text=&7클릭하여 확인 run_command=/enderchest %1% %2%)'
data_manager_system_buttons: '[시스템:](gray) [[⏷ 파일 덤프…]](dark_gray show_text=&7클릭하여 이 유저 데이터 스냅샷을 덤프하기\n&8데이터 덤프 파일은 ~/plugins/HuskSync/dumps/ 에서 찾을습니다. run_command=/husksync:userdata dump %1% %2% file) [[☂ 웹 덤프…]](dark_gray show_text=&7클릭하여 유저 데이터 스냅샷을 mc-log 서비스에 덤프하기\n&8데이터를 포함한 URL이 제공됩니다. run_command=/husksync:userdata dump %1% %2% web)' data_manager_management_buttons: '[관리:](gray) [[❌ 삭제…]](#ff3300 show_text=&7클릭하여 이 유저 스냅샷 데이터를 삭제\n&8이 기능은 현재 유저의 인벤토리 데이터에는 영향을 미치지 않습니다.\n&#ff3300&⚠ 이 작업은 되돌릴습니다! suggest_command=/husksync:userdata delete %1% %2%) [[⏪ 복구…]](#00fb9a show_text=&7클릭하여 유저 데이터 복구\n&8유저의 데이터가 이 스냅샷의 데이터로 변경됩니다.\n&#ff3300&⚠ %1%님의 현재 데이터에 덧씌워 집니다! suggest_command=/husksync:userdata restore %1% %2%) [[※ 고정/고정 해제…]](#d8ff2b show_text=&7클릭하여 유저 데이터를 고정 또는 고정 해제\n&8고정된 스냅샷은 자동적으로 갱신되지 않습니다. run_command=/userdata pin %1% %2%)'
data_manager_advancements_preview_remaining: '외 %1%개...' data_manager_system_buttons: '[시스템:](gray) [[⏷ 파일 덤프…]](dark_gray show_text=&7클릭하여 이 유저 데이터 스냅샷을 덤프하기\n&8데이터 덤프 파일은 ~/plugins/HuskSync/dumps/ 에서 찾을 수 있습니다. run_command=/husksync:userdata dump %1% %2% file) [[☂ 웹 덤프…]](dark_gray show_text=&7클릭하여 유저 데이터 스냅샷을 mc-log 서비스에 덤프하기\n&8데이터를 포함한 URL이 제공됩니다. run_command=/husksync:userdata dump %1% %2% web)'
data_list_title: '[%1%님의 유저 데이터 스냅샷 목록:](#00fb9a) [(%2%-%3% 중](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n' data_manager_advancements_preview_remaining: '외 %1%개...'
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_title: '[%1%님의 유저 데이터 스냅샷 목록:](#00fb9a) [(%2%-%3% 중](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n'
data_deleted: '[❌ 성공적으로](#00fb9a) [%3%](#00fb9a show_text=&7플레이어 UUID:\n&8%4%) [님의 유저 데이터 스냅샷](#00fb9a) [%1%](#00fb9a show_text=&7버전 UUID:\n&8%2%)[을 삭제하였습니다.](#00fb9a)' 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_restored: '[ 성공적으로 복구되었습니다.](#00fb9a) [%1%](#00fb9a show_text=&7플레이어 UUID:\n&8%2%)[님의 현재 유저 데이터 스냅샷](#00fb9a) [%3%](#00fb9a show_text=&7버전 UUID:\n&8%4%)[으로 변경되었습니다.](#00fb9a)' data_deleted: '[ 성공적으로](#00fb9a) [%3%](#00fb9a show_text=&7플레이어 UUID:\n&8%4%) [님의 유저 데이터 스냅샷](#00fb9a) [%1%](#00fb9a show_text=&7버전 UUID:\n&8%2%)[을 삭제하였습니다.](#00fb9a)'
data_pinned: '[ 성공적으로](#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_unpinned: '[※ 성공적으로](#00fb9a) [%3%](#00fb9a show_text=&7플레이어 UUID:\n&8%4%) [님의 유저 데이터 스냅샷](#00fb9a) [%1%](#00fb9a show_text=&7버전 UUID:\n&8%2%)[을 고정 해제하였습니다.](#00fb9a)' data_pinned: '[※ 성공적으로](#00fb9a) [%3%](#00fb9a show_text=&7플레이어 UUID:\n&8%4%)[님의 유저 데이터 스냅샷](#00fb9a) [%1%](#00fb9a show_text=&7버전 UUID:\n&8%2%)[을 고정하였습니다.](#00fb9a)'
data_dumped: '[ 성공적으로 %2%님의 유저 데이터 스냅샷 %1%를 다음으로 덤프하였습니다:](#00fb9a) &7%3%' data_unpinned: '[ 성공적으로](#00fb9a) [%3%](#00fb9a show_text=&7플레이어 UUID:\n&8%4%) [님의 유저 데이터 스냅샷](#00fb9a) [%1%](#00fb9a show_text=&7버전 UUID:\n&8%2%)[을 고정 해제하였습니다.](#00fb9a)'
list_footer: '\n%1%[페이지](#00fb9a) [%2%](#00fb9a)/[%3%](#00fb9a)%4% %5%' data_dumped: '[☂ 성공적으로 %2%님의 유저 데이터 스냅샷 %1%를 다음으로 덤프하였습니다:](#00fb9a) &7%3%'
list_previous_page_button: '[◀](white show_text=&7이전 페이지 보기 run_command=%2% %1%) ' list_footer: '\n%1%[페이지](#00fb9a) [%2%](#00fb9a)/[%3%](#00fb9a)%4% %5%'
list_next_page_button: ' [▶](white show_text=&7다음 페이지 보기 run_command=%2% %1%)' list_previous_page_button: '[◀](white show_text=&7이전 페이지 보기 run_command=%2% %1%) '
list_page_jumpers: '(%1%)' list_next_page_button: ' [▶](white show_text=&7다음 페이지 보기 run_command=%2% %1%)'
list_page_jumper_button: '[%1%](show_text=&7%1% 페이지 보기 run_command=%2% %1%)' list_page_jumpers: '(%1%)'
list_page_jumper_current_page: '[%1%](#00fb9a)' list_page_jumper_button: '[%1%](show_text=&7%1% 페이지 보기 run_command=%2% %1%)'
list_page_jumper_separator: ' ' list_page_jumper_current_page: '[%1%](#00fb9a)'
list_page_jumper_group_separator: '' list_page_jumper_separator: ' '
save_cause_disconnect: 'disconnect' list_page_jumper_group_separator: '…'
save_cause_world_save: 'world save' save_cause_disconnect: 'disconnect'
save_cause_death: 'death' save_cause_world_save: 'world save'
save_cause_server_shutdown: 'server shutdown' save_cause_death: 'death'
save_cause_inventory_command: 'inventory command' save_cause_server_shutdown: 'server shutdown'
save_cause_enderchest_command: 'enderchest command' save_cause_inventory_command: 'inventory command'
save_cause_backup_restore: 'backup restore' save_cause_enderchest_command: 'enderchest command'
save_cause_api: 'API' save_cause_backup_restore: 'backup restore'
save_cause_mpdb_migration: 'MPDB migration' save_cause_api: 'API'
save_cause_legacy_migration: 'legacy migration' save_cause_mpdb_migration: 'MPDB migration'
save_cause_converted_from_v2: 'converted from v2' save_cause_legacy_migration: 'legacy migration'
up_to_date: '[HuskSync](#00fb9a bold) [| 가장 최신 버전의 HuskSync를 실행 중입니다 (v%1%).](#00fb9a)' save_cause_converted_from_v2: 'converted from v2'
update_available: '[HuskSync](#ff7e5e bold) [| 새로운 버전의 HuskSync가 존재합니다: v%1% (현재 버전: v%2%).](#ff7e5e)' up_to_date: '[HuskSync](#00fb9a bold) [| 가장 최신 버전의 HuskSync를 실행 중입니다 (v%1%).](#00fb9a)'
reload_complete: '[HuskSync](#00fb9a bold) [| 콘피그와 메시지 파일을 다시 불러왔습니다.](#00fb9a)\n[⚠ 모든 서버의 컨피그 파일을 변경하였는지 확인하세요!](#00fb9a)\n[몇몇 설정은 재시작 후에 적용됩니다.](#00fb9a italic)' update_available: '[HuskSync](#ff7e5e bold) [| 새로운 버전의 HuskSync가 존재합니다: v%1% (현재 버전: v%2%).](#ff7e5e)'
system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)' reload_complete: '[HuskSync](#00fb9a bold) [| 콘피그와 메시지 파일을 다시 불러왔습니다.](#00fb9a)\n[⚠ 모든 서버의 컨피그 파일을 변경하였는지 확인하세요!](#00fb9a)\n[몇몇 설정은 재시작 후에 적용됩니다.](#00fb9a italic)'
error_invalid_syntax: '[오류:](#ff3300) [잘못된 사용법. 사용법:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&클릭하여 입력할 수 있습니다. suggest_command=%1%)' system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)'
error_invalid_player: '[오류:](#ff3300) [해당 이름의 사용자를 찾을 수 없습니다.](#ff7e5e)' error_invalid_syntax: '[오류:](#ff3300) [잘못된 사용법. 사용법:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&클릭하여 입력할 수 있습니다. suggest_command=%1%)'
error_no_permission: '[오류:](#ff3300) [해당 명령어를 사용할 권한이 없습니다.](#ff7e5e)' error_invalid_player: '[오류:](#ff3300) [해당 이름의 사용자를 찾을 수 없습니다.](#ff7e5e)'
error_console_command_only: '[오류:](#ff3300) [해당 명령어는 콘솔을 통해서만 사용할 수 있습니다.](#ff7e5e)' error_no_permission: '[오류:](#ff3300) [해당 명령어를 사용할 권한이 없습니다.](#ff7e5e)'
error_in_game_command_only: '오류: 해당 명령어는 게임 내부에서만 사용할 수 있습니다.' error_console_command_only: '[오류:](#ff3300) [해당 명령어는 콘솔을 통해서만 사용할 수 있습니다.](#ff7e5e)'
error_no_data_to_display: '[오류:](#ff3300) [표시할 유저 데이터를 찾을습니다.](#ff7e5e)' error_in_game_command_only: '오류: 해당 명령어는 게임 내부에서만 사용할습니다.'
error_invalid_version_uuid: '[오류:](#ff3300) [해당 버전 UUID의 유저 데이터 스냅샷을 찾을 수 없습니다.](#ff7e5e)' error_no_data_to_display: '[오류:](#ff3300) [표시할 유저 데이터 찾을 수 없습니다.](#ff7e5e)'
husksync_command_description: 'HuskSync 플러그인을 관리합니다.' error_invalid_version_uuid: '[오류:](#ff3300) [해당 버전 UUID의 유저 데이터 스냅샷을 찾을 수 없습니다.](#ff7e5e)'
userdata_command_description: '확인, 관리 또는 복구합니다.' husksync_command_description: 'HuskSync 플러그인을 관리합니다.'
inventory_command_description: '플레이어의 인벤토리를 열람 또는 편집합니다.' userdata_command_description: '확인, 관리 또는 복구합니다.'
enderchest_command_description: '플레이어의 엔더상자를 열람 또는 편집합니다.' inventory_command_description: '플레이어의 인벤토리를 열람 또는 편집합니다.'
enderchest_command_description: '플레이어의 엔더상자를 열람 또는 편집합니다.'

View File

@@ -1,62 +1,63 @@
synchronization_complete: '[⏵ Data gesynchroniseerd!](#00fb9a)' locales:
synchronization_failed: '[⏵ Synchroniseren van jouw gegevens is niet gelukt! Neem contact op met een beheerder.](#ff7e5e)' synchronization_complete: '[⏵ Data gesynchroniseerd!](#00fb9a)'
inventory_viewer_menu_title: '&0%1%''s Inventaris' synchronization_failed: '[⏵ Synchroniseren van jouw gegevens is niet gelukt! Neem contact op met een beheerder.](#ff7e5e)'
ender_chest_viewer_menu_title: '&0%1%''s Enderkist' inventory_viewer_menu_title: '&0%1%''s Inventaris'
inventory_viewer_opened: '[Momentopname bekijken van](#00fb9a) [%1%](#00fb9a bold)[''s inventaris per ⌚ %2%](#00fb9a)' ender_chest_viewer_menu_title: '&0%1%''s Enderkist'
ender_chest_viewer_opened: '[Momentopname bekijken van](#00fb9a) [%1%](#00fb9a bold)[''s Enderkist per ⌚ %2%](#00fb9a)' inventory_viewer_opened: '[Momentopname bekijken van](#00fb9a) [%1%](#00fb9a bold)[''s inventaris per ⌚ %2%](#00fb9a)'
data_update_complete: '[🔔 Jouw gegevens zijn bijgewerkt!](#00fb9a)' ender_chest_viewer_opened: '[Momentopname bekijken van](#00fb9a) [%1%](#00fb9a bold)[''s Enderkist per ⌚ %2%](#00fb9a)'
data_update_failed: '[🔔 Het is niet gelukt om jouw gegevens bij te werken! Neem contact op met een beheerder.](#ff7e5e)' data_update_complete: '[🔔 Jouw gegevens zijn bijgewerkt!](#00fb9a)'
user_registration_complete: '[⭐ Gebruikersregistratie voltooid!](#00fb9a)' data_update_failed: '[🔔 Het is niet gelukt om jouw gegevens bij te werken! Neem contact op met een beheerder.](#ff7e5e)'
data_manager_title: '[Momentopname van gebruikersgegevens bekijken](#00fb9a) [%1%](#00fb9a show_text=&7Versie UUID:\n&8%2%) [voor](#00fb9a) [%3%](#00fb9a bold show_text=&7Speler UUID:\n&8%4%)[:](#00fb9a)' user_registration_complete: '[⭐ Gebruikersregistratie voltooid!](#00fb9a)'
data_manager_timestamp: '[⌚ %1%](#ffc43b-#f5c962 show_text=&7Versie tijdmarkering:\n&8Toen de gegevens werden opgeslagen)' data_manager_title: '[Momentopname van gebruikersgegevens bekijken](#00fb9a) [%1%](#00fb9a show_text=&7Versie UUID:\n&8%2%) [voor](#00fb9a) [%3%](#00fb9a bold show_text=&7Speler UUID:\n&8%4%)[:](#00fb9a)'
data_manager_pinned: '[※ Momentopname vastgezet](#d8ff2b show_text=&7Vastgezet:\n&8Deze momentopname van gebruikersgegevens wordt niet automatisch gerouleerd.)' data_manager_timestamp: '[⌚ %1%](#ffc43b-#f5c962 show_text=&7Versie tijdmarkering:\n&8Toen de gegevens werden opgeslagen)'
data_manager_cause: '[⚑ %1%](#23a825-#36f539 show_text=&7Reden opslaan:\n&8Waarom de data is opgeslagen)' data_manager_pinned: '[※ Momentopname vastgezet](#d8ff2b show_text=&7Vastgezet:\n&8Deze momentopname van gebruikersgegevens wordt niet automatisch gerouleerd.)'
data_manager_server: '[ %1%](#ff87b3-#f5538e show_text=&7Server:\n&8Name of the server the data was saved on)' data_manager_cause: '[ %1%](#23a825-#36f539 show_text=&7Reden opslaan:\n&8Waarom de data is opgeslagen)'
data_manager_size: '[ %1%](color=#62a9f5-#7ab8fa show_text=&7Grootte van momentopname:\n&8Geschatte bestandsgrootte van de momentopname (in KiB))\n' data_manager_server: '[ %1%](#ff87b3-#f5538e show_text=&7Server:\n&8Name of the server the data was saved on)'
data_manger_status: '[%1%](red)[/](gray)[%2%](red)[×](gray)[❤](red show_text=&7Gezondheids punten) [%3%](yellow)[×](gray)[🍖](yellow show_text=&7Honger punten) [ʟᴠ](green)[.](gray)[%4%](green show_text=&7XP level) [🏹 %5%](dark_aqua show_text=&7Speltype)' data_manager_size: '[%1%](color=#62a9f5-#7ab8fa show_text=&7Grootte van momentopname:\n&8Geschatte bestandsgrootte van de momentopname (in KiB))\n'
data_manager_advancements_statistics: '[⭐ Advancements: %1%](color=#ffc43b-#f5c962 show_text=&7Advancements waarin je voortgang hebt:\n&8%2%) [⌛ Speeltijd: %3%uren](color=#62a9f5-#7ab8fa show_text=&7In-game speeltijd\n&8⚠ Gebaseerd op in-game statistieken)\n' data_manger_status: '[%1%](red)[/](gray)[%2%](red)[×](gray)[❤](red show_text=&7Gezondheids punten) [%3%](yellow)[×](gray)[🍖](yellow show_text=&7Honger punten) [ʟᴠ](green)[.](gray)[%4%](green show_text=&7XP level) [🏹 %5%](dark_aqua show_text=&7Speltype)'
data_manager_item_buttons: '[View:](gray) [[🪣 Inventaris…]](color=#a17b5f-#f5b98c show_text=&7Klikken om te bekijken run_command=/inventory %1% %2%) [[⌀ Enderkist…]](#b649c4-#d254ff show_text=&7Klikken om te bekijken run_command=/enderchest %1% %2%)' data_manager_advancements_statistics: '[⭐ Advancements: %1%](color=#ffc43b-#f5c962 show_text=&7Advancements waarin je voortgang hebt:\n&8%2%) [⌛ Speeltijd: %3%uren](color=#62a9f5-#7ab8fa show_text=&7In-game speeltijd\n&8⚠ Gebaseerd op in-game statistieken)\n'
data_manager_management_buttons: '[Beheren:](gray) [[❌ Verwijderen…]](#ff3300 show_text=&7Klik om deze momentopname van gebruikersgegevens te verwijderen.\n&8Dit heeft geen invloed op de huidige gegevens van de gebruiker.\n&#ff3300&⚠ Dit kan niet ongedaan gemaakt worden! suggest_command=/husksync:userdata delete %1% %2%) [[⏪ Herstellen…]](#00fb9a show_text=&7Klik om deze gebruikersgegevens te herstellen.\n&8Hierdoor worden de gegevens van de gebruiker ingesteld op deze momentopname.\n&#ff3300&⚠ %1%''s huidige gegevens worden overschreven! suggest_command=/husksync:userdata restore %1% %2%) [[※ Vastzetten/losmaken…]](#d8ff2b show_text=&7Klik om deze momentopname van gebruikersgegevens vast te zetten of los te maken\n&8Vastgezette momentopnamen worden niet automatisch gerouleerd run_command=/userdata pin %1% %2%)' data_manager_item_buttons: '[View:](gray) [[🪣 Inventaris…]](color=#a17b5f-#f5b98c show_text=&7Klikken om te bekijken run_command=/inventory %1% %2%) [[⌀ Enderkist…]](#b649c4-#d254ff show_text=&7Klikken om te bekijken run_command=/enderchest %1% %2%)'
data_manager_system_buttons: '[Systeem:](gray) [[⏷ Bestandsdump…]](dark_gray show_text=&7Klik om deze ruwe gebruikersgegevenssnapshot naar een bestand te dumperen.\n&8Gegevensdumps zijn te vinden in ~/plugins/HuskSync/dumps/ run_command=/husksync:userdata dump %1% %2% file) [[☂ Webdump…]](dark_gray show_text=&7Klik om deze ruwe gebruikersgegevenssnapshot naar de mc-logs-service te dumpen\n&8Je ontvangt een URL met de gegevens. run_command=/husksync:userdata dump %1% %2% web)' data_manager_management_buttons: '[Beheren:](gray) [[❌ Verwijderen…]](#ff3300 show_text=&7Klik om deze momentopname van gebruikersgegevens te verwijderen.\n&8Dit heeft geen invloed op de huidige gegevens van de gebruiker.\n&#ff3300&⚠ Dit kan niet ongedaan gemaakt worden! suggest_command=/husksync:userdata delete %1% %2%) [[⏪ Herstellen…]](#00fb9a show_text=&7Klik om deze gebruikersgegevens te herstellen.\n&8Hierdoor worden de gegevens van de gebruiker ingesteld op deze momentopname.\n&#ff3300&⚠ %1%''s huidige gegevens worden overschreven! suggest_command=/husksync:userdata restore %1% %2%) [[※ Vastzetten/losmaken…]](#d8ff2b show_text=&7Klik om deze momentopname van gebruikersgegevens vast te zetten of los te maken\n&8Vastgezette momentopnamen worden niet automatisch gerouleerd run_command=/userdata pin %1% %2%)'
data_manager_advancements_preview_remaining: 'en %1% meer…' data_manager_system_buttons: '[Systeem:](gray) [[⏷ Bestandsdump…]](dark_gray show_text=&7Klik om deze ruwe gebruikersgegevenssnapshot naar een bestand te dumperen.\n&8Gegevensdumps zijn te vinden in ~/plugins/HuskSync/dumps/ run_command=/husksync:userdata dump %1% %2% file) [[☂ Webdump…]](dark_gray show_text=&7Klik om deze ruwe gebruikersgegevenssnapshot naar de mc-logs-service te dumpen\n&8Je ontvangt een URL met de gegevens. run_command=/husksync:userdata dump %1% %2% web)'
data_list_title: '[%1%''s momentopnamen van gebruikersgegevens:](#00fb9a) [(%2%-%3% van](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n' data_manager_advancements_preview_remaining: 'en %1% meer…'
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_title: '[%1%''s momentopnamen van gebruikersgegevens:](#00fb9a) [(%2%-%3% van](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n'
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_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_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_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_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%)' 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_unpinned: '[※ Momentopname van gebruikersgegevens is losgemaakt](#00fb9a) [%1%](#00fb9a show_text=&7Versie UUID:\n&8%2%) [voor](#00fb9a) [%3%.](#00fb9a show_text=&7Speler 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%)'
data_dumped: '[☂ De momentopname van gebruikersgegevens %1% voor %2% is met succes gedumpt naar:](#00fb9a) &7%3%' data_unpinned: '[※ Momentopname van gebruikersgegevens is losgemaakt](#00fb9a) [%1%](#00fb9a show_text=&7Versie UUID:\n&8%2%) [voor](#00fb9a) [%3%.](#00fb9a show_text=&7Speler UUID:\n&8%4%)'
list_footer: '\n%1%[Pagina](#00fb9a) [%2%](#00fb9a)/[%3%](#00fb9a)%4% %5%' data_dumped: '[☂ De momentopname van gebruikersgegevens %1% voor %2% is met succes gedumpt naar:](#00fb9a) &7%3%'
list_previous_page_button: '[◀](white show_text=&7Bekijk vorige pagina run_command=%2% %1%) ' list_footer: '\n%1%[Pagina](#00fb9a) [%2%](#00fb9a)/[%3%](#00fb9a)%4% %5%'
list_next_page_button: ' [▶](white show_text=&7Bekijk volgende pagina run_command=%2% %1%)' list_previous_page_button: '[◀](white show_text=&7Bekijk vorige pagina run_command=%2% %1%) '
list_page_jumpers: '(%1%)' list_next_page_button: ' [▶](white show_text=&7Bekijk volgende pagina run_command=%2% %1%)'
list_page_jumper_button: '[%1%](show_text=&7Ga naar pagina %1% run_command=%2% %1%)' list_page_jumpers: '(%1%)'
list_page_jumper_current_page: '[%1%](#00fb9a)' list_page_jumper_button: '[%1%](show_text=&7Ga naar pagina %1% run_command=%2% %1%)'
list_page_jumper_separator: ' ' list_page_jumper_current_page: '[%1%](#00fb9a)'
list_page_jumper_group_separator: '' list_page_jumper_separator: ' '
save_cause_disconnect: 'disconnect' list_page_jumper_group_separator: '…'
save_cause_world_save: 'world save' save_cause_disconnect: 'disconnect'
save_cause_death: 'death' save_cause_world_save: 'world save'
save_cause_server_shutdown: 'server shutdown' save_cause_death: 'death'
save_cause_inventory_command: 'inventory command' save_cause_server_shutdown: 'server shutdown'
save_cause_enderchest_command: 'enderchest command' save_cause_inventory_command: 'inventory command'
save_cause_backup_restore: 'backup restore' save_cause_enderchest_command: 'enderchest command'
save_cause_api: 'API' save_cause_backup_restore: 'backup restore'
save_cause_mpdb_migration: 'MPDB migration' save_cause_api: 'API'
save_cause_legacy_migration: 'legacy migration' save_cause_mpdb_migration: 'MPDB migration'
save_cause_converted_from_v2: 'converted from v2' save_cause_legacy_migration: 'legacy migration'
up_to_date: '[HuskSync](#00fb9a bold) [| Je gebruikt de nieuwste versie van HuskSync (v%1%).](#00fb9a)' save_cause_converted_from_v2: 'converted from v2'
update_available: '[HuskSync](#ff7e5e bold) [| Er is een nieuwe versie van HuskSync beschikbaar: v%1% (huidige versie: v%2%).](#ff7e5e)' up_to_date: '[HuskSync](#00fb9a bold) [| Je gebruikt de nieuwste versie van HuskSync (v%1%).](#00fb9a)'
reload_complete: '[HuskSync](#00fb9a bold) [| Configuratie- en berichtbestanden opnieuw geladen.](#00fb9a)\n[⚠ Controleer of de configuratiebestanden up-to-date zijn op alle servers!](#00fb9a)\n[Een herstart is nodig voor de configuratiewijzigingen van kracht te laten worden.](#00fb9a italic)' update_available: '[HuskSync](#ff7e5e bold) [| Er is een nieuwe versie van HuskSync beschikbaar: v%1% (huidige versie: v%2%).](#ff7e5e)'
system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)' reload_complete: '[HuskSync](#00fb9a bold) [| Configuratie- en berichtbestanden opnieuw geladen.](#00fb9a)\n[⚠ Controleer of de configuratiebestanden up-to-date zijn op alle servers!](#00fb9a)\n[Een herstart is nodig voor de configuratiewijzigingen van kracht te laten worden.](#00fb9a italic)'
error_invalid_syntax: '[Error:](#ff3300) [Onjuiste syntaxis. Gebruik:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)' system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)'
error_invalid_player: '[Error:](#ff3300) [Kan geen speler met die naam vinden.](#ff7e5e)' error_invalid_syntax: '[Error:](#ff3300) [Onjuiste syntaxis. Gebruik:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)'
error_no_permission: '[Error:](#ff3300) [Je hebt geen toestemming om deze opdracht uit te voeren](#ff7e5e)' error_invalid_player: '[Error:](#ff3300) [Kan geen speler met die naam vinden.](#ff7e5e)'
error_console_command_only: '[Error:](#ff3300) [Dat command kan alleen via de console worden uitgevoerd](#ff7e5e)' error_no_permission: '[Error:](#ff3300) [Je hebt geen toestemming om deze opdracht uit te voeren](#ff7e5e)'
error_in_game_command_only: 'Error: Dat command kan alleen in-game worden gebruikt.' error_console_command_only: '[Error:](#ff3300) [Dat command kan alleen via de console worden uitgevoerd](#ff7e5e)'
error_no_data_to_display: '[Error:](#ff3300) [Kon geen gebruikersgegevens vinden om weer te geven.](#ff7e5e)' error_in_game_command_only: 'Error: Dat command kan alleen in-game worden gebruikt.'
error_invalid_version_uuid: '[Error:](#ff3300) [Kon geen gebruikersgegevens vinden voor dat versie-UUID.](#ff7e5e)' error_no_data_to_display: '[Error:](#ff3300) [Kon geen gebruikersgegevens vinden om weer te geven.](#ff7e5e)'
husksync_command_description: 'Beheer de HuskSync plugin' error_invalid_version_uuid: '[Error:](#ff3300) [Kon geen gebruikersgegevens vinden voor dat versie-UUID.](#ff7e5e)'
userdata_command_description: 'Bekijk, beheer en herstel de gebruikersgegevens van spelers' husksync_command_description: 'Beheer de HuskSync plugin'
inventory_command_description: 'Bekijk en bewerk de inventaris van een speler' userdata_command_description: 'Bekijk, beheer en herstel de gebruikersgegevens van spelers'
enderchest_command_description: 'Bekijk en bewerk de Enderkist van een speler' inventory_command_description: 'Bekijk en bewerk de inventaris van een speler'
enderchest_command_description: 'Bekijk en bewerk de Enderkist van een speler'

View File

@@ -1,62 +1,63 @@
synchronization_complete: '[⏵ Dados sincronizados!](#00fb9a)' locales:
synchronization_failed: '[⏵ Falha na sincronização de seus dados! Por favor entre em contato com um administrador.](#ff7e5e)' synchronization_complete: '[⏵ Dados sincronizados!](#00fb9a)'
inventory_viewer_menu_title: '&0%1%''s Inventory' synchronization_failed: '[⏵ Falha na sincronização de seus dados! Por favor entre em contato com um administrador.](#ff7e5e)'
ender_chest_viewer_menu_title: '&0%1%''s Ender Chest' inventory_viewer_menu_title: '&0%1%''s Inventory'
inventory_viewer_opened: '[Visualizando snapshot de](#00fb9a) [%1%](#00fb9a bold) [''s inventory a partir de ⌚ %2%](#00fb9a)' ender_chest_viewer_menu_title: '&0%1%''s Ender Chest'
ender_chest_viewer_opened: '[Visualizando snapshot de](#00fb9a) [%1%](#00fb9a bold) [''s Ender Chest a partir de ⌚ %2%](#00fb9a)' inventory_viewer_opened: '[Visualizando snapshot de](#00fb9a) [%1%](#00fb9a bold) [''s inventory a partir de ⌚ %2%](#00fb9a)'
data_update_complete: '[🔔 Seus dados foram atualizados!](#00fb9a)' ender_chest_viewer_opened: '[Visualizando snapshot de](#00fb9a) [%1%](#00fb9a bold) [''s Ender Chest a partir de ⌚ %2%](#00fb9a)'
data_update_failed: '[🔔 Falha na atualização de seus dados! Por favor entre em contato com um administrador.](#ff7e5e)' data_update_complete: '[🔔 Seus dados foram atualizados!](#00fb9a)'
user_registration_complete: '[⭐ User registration complete!](#00fb9a)' data_update_failed: '[🔔 Falha na atualização de seus dados! Por favor entre em contato com um administrador.](#ff7e5e)'
data_manager_title: '[Visualizando snapshot dos dados do usuário](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\n&8%2%) [for](#00fb9a) [%3%](#00fb9a bold show_text=&7Player UUID:\n&8%4%)[:](#00fb9a)' user_registration_complete: '[⭐ User registration complete!](#00fb9a)'
data_manager_timestamp: '[⌚ %1%](#ffc43b-#f5c962 show_text=&7Version timestamp:\n&8Quando os dados foram salvos)' data_manager_title: '[Visualizando snapshot dos dados do usuário](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\n&8%2%) [for](#00fb9a) [%3%](#00fb9a bold show_text=&7Player UUID:\n&8%4%)[:](#00fb9a)'
data_manager_pinned: '[※ Snapshot marcada](#d8ff2b show_text=&7Marcada:\n&8Essa snapshot de dados do usuário não será girada automaticamente.)' data_manager_timestamp: '[⌚ %1%](#ffc43b-#f5c962 show_text=&7Version timestamp:\n&8Quando os dados foram salvos)'
data_manager_cause: '[⚑ %1%](#23a825-#36f539 show_text=&7Causa do salvamento:\n&8O motivo para que os dados fossem salvos)' data_manager_pinned: '[※ Snapshot marcada](#d8ff2b show_text=&7Marcada:\n&8Essa snapshot de dados do usuário não será girada automaticamente.)'
data_manager_server: '[ %1%](#ff87b3-#f5538e show_text=&7Server:\n&8Name of the server the data was saved on)' data_manager_cause: '[ %1%](#23a825-#36f539 show_text=&7Causa do salvamento:\n&8O motivo para que os dados fossem salvos)'
data_manager_size: '[ %1%](color=#62a9f5-#7ab8fa show_text=&7Snapshot size:\n&8Estimated file size of the snapshot (in KiB))\n' data_manager_server: '[ %1%](#ff87b3-#f5538e show_text=&7Server:\n&8Name of the server the data was saved on)'
data_manger_status: '[%1%](red)[/](gray)[%2%](red)[×](gray)[❤](red show_text=&7Pontos de Vida) [%3%](yellow)[×](gray)[🍖](yellow show_text=&7Pontos de vida) [ʟᴠ](green)[.](gray)[%4%](green show_text=&7XP level) [🏹 %5%](dark_aqua show_text=&7Game mode)' data_manager_size: '[%1%](color=#62a9f5-#7ab8fa show_text=&7Snapshot size:\n&8Estimated file size of the snapshot (in KiB))\n'
data_manager_advancements_statistics: '[⭐ Progressos: %1%](color=#ffc43b-#f5c962 show_text=&7Progressos que você tem realizado em:\n&8%2%) [⌛ Tempo de jogo: %3%ʜʀs](color=#62a9f5-#7ab8fa show_text=&7Tempo de jogo dentro do jogo\n&8⚠ Com base em estatísticas dentro do jogo)\n' data_manger_status: '[%1%](red)[/](gray)[%2%](red)[×](gray)[❤](red show_text=&7Pontos de Vida) [%3%](yellow)[×](gray)[🍖](yellow show_text=&7Pontos de vida) [ʟᴠ](green)[.](gray)[%4%](green show_text=&7XP level) [🏹 %5%](dark_aqua show_text=&7Game mode)'
data_manager_item_buttons: '[View:](gray) [[🪣 Inventory…]](color=#a17b5f-#f5b98c show_text=&7Clique para ver run_command=/inventory %1% %2%) [[⌀ Ender Chest…]](#b649c4-#d254ff show_text=&7Clique para ver run_command=/enderchest %1% %2%)' data_manager_advancements_statistics: '[⭐ Progressos: %1%](color=#ffc43b-#f5c962 show_text=&7Progressos que você tem realizado em:\n&8%2%) [⌛ Tempo de jogo: %3%ʜʀs](color=#62a9f5-#7ab8fa show_text=&7Tempo de jogo dentro do jogo\n&8⚠ Com base em estatísticas dentro do jogo)\n'
data_manager_management_buttons: '[Gerenciar:](gray) [[❌ Deletar…]](#ff3300 show_text=&7Clique para deletar esta snapshot de dados do usuário\n&8Isto não afetará os dados atuais do usuário.\n&#ff3300&⚠ Isto não pode ser desfeito! suggest_command=/husksync:userdata delete %1% %2%) [[⏪ Restaurar…]](#00fb9a show_text=&7Clique para restaurar estes dados do usuário.\n&8Isto substituirá os dados atuais do usuário para os da snapshot.\n&#ff3300&⚠ %1%''s os dados atuais serão substituídos! suggest_command=/husksync:userdata restore %1% %2%) [[※ Marcar/Desmarcar…]](#d8ff2b show_text=&7Clique para marcar ou desmarcar este snapshot de dados do usuário\n&8Snapshots marcadas não serão giradas automaticamente run_command=/userdata pin %1% %2%)' data_manager_item_buttons: '[View:](gray) [[🪣 Inventory…]](color=#a17b5f-#f5b98c show_text=&7Clique para ver run_command=/inventory %1% %2%) [[⌀ Ender Chest…]](#b649c4-#d254ff show_text=&7Clique para ver run_command=/enderchest %1% %2%)'
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_management_buttons: '[Gerenciar:](gray) [[❌ Deletar…]](#ff3300 show_text=&7Clique para deletar esta snapshot de dados do usuário\n&8Isto não afetará os dados atuais do usuário.\n&#ff3300&⚠ Isto não pode ser desfeito! suggest_command=/husksync:userdata delete %1% %2%) [[⏪ Restaurar…]](#00fb9a show_text=&7Clique para restaurar estes dados do usuário.\n&8Isto substituirá os dados atuais do usuário para os da snapshot.\n&#ff3300&⚠ %1%''s os dados atuais serão substituídos! suggest_command=/husksync:userdata restore %1% %2%) [[※ Marcar/Desmarcar…]](#d8ff2b show_text=&7Clique para marcar ou desmarcar este snapshot de dados do usuário\n&8Snapshots marcadas não serão giradas automaticamente run_command=/userdata pin %1% %2%)'
data_manager_advancements_preview_remaining: 'e %1% mais…' 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_list_title: '[%1%''s user data snapshots:](#00fb9a) [(%2%-%3% of](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n' data_manager_advancements_preview_remaining: 'e %1% mais…'
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_title: '[%1%''s user data snapshots:](#00fb9a) [(%2%-%3% of](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n'
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_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_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_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_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%)' 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_unpinned: '[※ Snapshot de dados do usuário desmarcada com sucesso](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\n&8%2%) [for](#00fb9a) [%3%.](#00fb9a show_text=&7Player 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%)'
data_dumped: '[ Successfully dumped the user data snapshot %1% for %2% to:](#00fb9a) &7%3%' data_unpinned: '[ Snapshot de dados do usuário desmarcada com sucesso](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\n&8%2%) [for](#00fb9a) [%3%.](#00fb9a show_text=&7Player UUID:\n&8%4%)'
list_footer: '\n%1%[Page](#00fb9a) [%2%](#00fb9a)/[%3%](#00fb9a)%4% %5%' data_dumped: '[☂ Successfully dumped the user data snapshot %1% for %2% to:](#00fb9a) &7%3%'
list_previous_page_button: '[◀](white show_text=&7View previous page run_command=%2% %1%) ' list_footer: '\n%1%[Page](#00fb9a) [%2%](#00fb9a)/[%3%](#00fb9a)%4% %5%'
list_next_page_button: ' [▶](white show_text=&7View next page run_command=%2% %1%)' list_previous_page_button: '[◀](white show_text=&7View previous page run_command=%2% %1%) '
list_page_jumpers: '(%1%)' list_next_page_button: ' [▶](white show_text=&7View next page run_command=%2% %1%)'
list_page_jumper_button: '[%1%](show_text=&7Jump to page %1% run_command=%2% %1%)' list_page_jumpers: '(%1%)'
list_page_jumper_current_page: '[%1%](#00fb9a)' list_page_jumper_button: '[%1%](show_text=&7Jump to page %1% run_command=%2% %1%)'
list_page_jumper_separator: ' ' list_page_jumper_current_page: '[%1%](#00fb9a)'
list_page_jumper_group_separator: '' list_page_jumper_separator: ' '
save_cause_disconnect: 'disconnect' list_page_jumper_group_separator: '…'
save_cause_world_save: 'world save' save_cause_disconnect: 'disconnect'
save_cause_death: 'death' save_cause_world_save: 'world save'
save_cause_server_shutdown: 'server shutdown' save_cause_death: 'death'
save_cause_inventory_command: 'inventory command' save_cause_server_shutdown: 'server shutdown'
save_cause_enderchest_command: 'enderchest command' save_cause_inventory_command: 'inventory command'
save_cause_backup_restore: 'backup restore' save_cause_enderchest_command: 'enderchest command'
save_cause_api: 'API' save_cause_backup_restore: 'backup restore'
save_cause_mpdb_migration: 'MPDB migration' save_cause_api: 'API'
save_cause_legacy_migration: 'legacy migration' save_cause_mpdb_migration: 'MPDB migration'
save_cause_converted_from_v2: 'converted from v2' save_cause_legacy_migration: 'legacy migration'
up_to_date: '[HuskSync](#00fb9a bold) [| You are running the latest version of HuskSync (v%1%).](#00fb9a)' save_cause_converted_from_v2: 'converted from v2'
update_available: '[HuskSync](#ff7e5e bold) [| A new version of HuskSync is available: v%1% (running: v%2%).](#ff7e5e)' up_to_date: '[HuskSync](#00fb9a bold) [| You are running the latest version of HuskSync (v%1%).](#00fb9a)'
reload_complete: '[HuskSync](#00fb9a bold) [| Arquivos de configuração e mensagens recarregados.](#00fb9a)\n[⚠ Ensure config files are up-to-date on all servers!](#00fb9a)\n[A restart is needed for config changes to take effect.](#00fb9a italic)' update_available: '[HuskSync](#ff7e5e bold) [| A new version of HuskSync is available: v%1% (running: v%2%).](#ff7e5e)'
system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)' reload_complete: '[HuskSync](#00fb9a bold) [| Arquivos de configuração e mensagens recarregados.](#00fb9a)\n[⚠ Ensure config files are up-to-date on all servers!](#00fb9a)\n[A restart is needed for config changes to take effect.](#00fb9a italic)'
error_invalid_syntax: '[Error:](#ff3300) [Sintaxe incorreta. Utilize:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)' system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)'
error_invalid_player: '[Error:](#ff3300) [Não foi possível encontrar um jogador com esse nome.](#ff7e5e)' error_invalid_syntax: '[Error:](#ff3300) [Sintaxe incorreta. Utilize:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)'
error_no_permission: '[Error:](#ff3300) [Você não tem permissão para executar este comando](#ff7e5e)' error_invalid_player: '[Error:](#ff3300) [Não foi possível encontrar um jogador com esse nome.](#ff7e5e)'
error_console_command_only: '[Error:](#ff3300) [Esse comando só pode ser executado através do console](#ff7e5e)' error_no_permission: '[Error:](#ff3300) [Você não tem permissão para executar este comando](#ff7e5e)'
error_in_game_command_only: 'Error: Esse comando só pode ser usado dentro do jogo.' error_console_command_only: '[Error:](#ff3300) [Esse comando só pode ser executado através do console](#ff7e5e)'
error_no_data_to_display: '[Error:](#ff3300) [Não encontramos nenhuma informação deste jogador para exibir.](#ff7e5e)' error_in_game_command_only: 'Error: Esse comando só pode ser usado dentro do jogo.'
error_invalid_version_uuid: '[Error:](#ff3300) [Não foi possível encontrar nenhuma informação deste jogador para essa versão UUID.](#ff7e5e)' error_no_data_to_display: '[Error:](#ff3300) [Não encontramos nenhuma informação deste jogador para exibir.](#ff7e5e)'
husksync_command_description: 'Manage the HuskSync plugin' error_invalid_version_uuid: '[Error:](#ff3300) [Não foi possível encontrar nenhuma informação deste jogador para essa versão UUID.](#ff7e5e)'
userdata_command_description: 'View, manage & restore player userdata' husksync_command_description: 'Manage the HuskSync plugin'
inventory_command_description: 'View & edit a player''s inventory' userdata_command_description: 'View, manage & restore player userdata'
enderchest_command_description: 'View & edit a player''s Ender Chest' inventory_command_description: 'View & edit a player''s inventory'
enderchest_command_description: 'View & edit a player''s Ender Chest'

View File

@@ -1,62 +1,63 @@
synchronization_complete: '[⏵ Данные синхронизированы!](#00fb9a)' locales:
synchronization_failed: '[⏵ Не удалось синхронизировать данные! Пожалуйста, обратитесь к администратору.](#ff7e5e)' synchronization_complete: '[⏵ Данные синхронизированы!](#00fb9a)'
inventory_viewer_menu_title: '&0Инвентарь %1%' synchronization_failed: '[⏵ Не удалось синхронизировать данные! Пожалуйста, обратитесь к администратору.](#ff7e5e)'
ender_chest_viewer_menu_title: '&0Эндер-сундук %1%' inventory_viewer_menu_title: '&0Инвентарь %1%'
inventory_viewer_opened: '[Просмотр снимка инвентаря](#00fb9a) [%1%](#00fb9a bold) [во время ⌚ %2%](#00fb9a)' ender_chest_viewer_menu_title: '&0Эндер-сундук %1%'
ender_chest_viewer_opened: '[Просмотр снимка эндер-сундука](#00fb9a) [%1%](#00fb9a bold) [во время ⌚ %2%](#00fb9a)' inventory_viewer_opened: '[Просмотр снимка инвентаря](#00fb9a) [%1%](#00fb9a bold) [во время ⌚ %2%](#00fb9a)'
data_update_complete: '[🔔 Ваши данные обновлены!](#00fb9a)' ender_chest_viewer_opened: '[Просмотр снимка эндер-сундука](#00fb9a) [%1%](#00fb9a bold) [во время ⌚ %2%](#00fb9a)'
data_update_failed: '[🔔 Не удалось обновить ваши данные! Пожалуйста, обратитесь к администратору.](#ff7e5e)' data_update_complete: '[🔔 Ваши данные обновлены!](#00fb9a)'
user_registration_complete: '[⭐ Регистрация пользователя завершена!](#00fb9a)' data_update_failed: '[🔔 Не удалось обновить ваши данные! Пожалуйста, обратитесь к администратору.](#ff7e5e)'
data_manager_title: '[Просмотр снимка данных](#00fb9a) [%1%](#00fb9a show_text=&7UUID снимка:\n&8%2%) [пользователя](#00fb9a) [%3%](#00fb9a bold show_text=&7UUID игрока:\n&8%4%)[:](#00fb9a)' user_registration_complete: '[⭐ Регистрация пользователя завершена!](#00fb9a)'
data_manager_timestamp: '[⌚ %1%](#ffc43b-#f5c962 show_text=&7Время:\n&8Когда данные были сохранены)' data_manager_title: '[Просмотр снимка данных](#00fb9a) [%1%](#00fb9a show_text=&7UUID снимка:\n&8%2%) [пользователя](#00fb9a) [%3%](#00fb9a bold show_text=&7UUID игрока:\n&8%4%)[:](#00fb9a)'
data_manager_pinned: '[※ Снимок закреплен](#d8ff2b show_text=&7Закреплен:\n&8Этот снимок данных пользователя не будет автоматически применено.)' data_manager_timestamp: '[⌚ %1%](#ffc43b-#f5c962 show_text=&7Время:\n&8Когда данные были сохранены)'
data_manager_cause: '[⚑ %1%](#23a825-#36f539 show_text=&7Причина сохранения:\n&8Что привело к сохранению данных)' data_manager_pinned: '[※ Снимок закреплен](#d8ff2b show_text=&7Закреплен:\n&8Этот снимок данных пользователя не будет автоматически применено.)'
data_manager_server: '[ %1%](#ff87b3-#f5538e show_text=&7Сервер:\n&8Название сервера где данные были сохранены)' data_manager_cause: '[ %1%](#23a825-#36f539 show_text=&7Причина сохранения:\n&8Что привело к сохранению данных)'
data_manager_size: '[ %1%](color=#62a9f5-#7ab8fa show_text=&7Размер:\n&8Предполагаемый размер снимка (в килобайтах))\n' data_manager_server: '[ %1%](#ff87b3-#f5538e show_text=&7Сервер:\n&8Название сервера где данные были сохранены)'
data_manger_status: '[%1%](red)[/](gray)[%2%](red)[×](gray)[❤](red show_text=&7Здоровье) [%3%](yellow)[×](gray)[🍖](yellow show_text=&7Голод) [ʟᴠ](green)[.](gray)[%4%](green show_text=&7Уровень опыта) [🏹 %5%](dark_aqua show_text=&7Игровой режим)' data_manager_size: '[%1%](color=#62a9f5-#7ab8fa show_text=&7Размер:\n&8Предполагаемый размер снимка (в килобайтах))\n'
data_manager_advancements_statistics: '[⭐ Достижения: %1%](color=#ffc43b-#f5c962 show_text=&7Полученные вами достижения:\n&8%2%) [⌛ Отыгранное время: %3%ʜʀs](color=#62a9f5-#7ab8fa show_text=&7Сколько времени вы отыграли\n&8⚠ Строится по внутреигровой статистике)\n' data_manger_status: '[%1%](red)[/](gray)[%2%](red)[×](gray)[❤](red show_text=&7Здоровье) [%3%](yellow)[×](gray)[🍖](yellow show_text=&7Голод) [ʟᴠ](green)[.](gray)[%4%](green show_text=&7Уровень опыта) [🏹 %5%](dark_aqua show_text=&7Игровой режим)'
data_manager_item_buttons: '[Просмотр:](gray) [[🪣 Инвентарь…]](color=#a17b5f-#f5b98c show_text=&7Нажмите для просмотра run_command=/inventory %1% %2%) [[⌀ Эндер-сундук…]](#b649c4-#d254ff show_text=&7Нажмите для просмотра run_command=/enderchest %1% %2%)' data_manager_advancements_statistics: '[⭐ Достижения: %1%](color=#ffc43b-#f5c962 show_text=&7Полученные вами достижения:\n&8%2%) [⌛ Отыгранное время: %3%ʜʀs](color=#62a9f5-#7ab8fa show_text=&7Сколько времени вы отыграли\n&8⚠ Строится по внутреигровой статистике)\n'
data_manager_management_buttons: '[Управление:](gray) [[❌ Удалить…]](#ff3300 show_text=&7Нажмите для удаления этого снимка данных пользователя\n&8Не влияет на текущие данные пользователя.\n&#ff3300&⚠ Необратимое действие! suggest_command=/husksync:userdata delete %1% %2%) [[⏪ Восстановить…]](#00fb9a show_text=&7Нажмите для восстановления данных пользователя\n&8Это восстановит данные пользователя до этого снимка.\n&#ff3300&⚠ Текущие данные %1% будут перезаписаны! suggest_command=/husksync:userdata restore %1% %2%) [[※ Закрепить/Открепить…]](#d8ff2b show_text=&7Нажмите для закрепления или открепления снимка данных пользователя\n&8Закрепленные снимки данных не удаляются автоматически. run_command=/userdata pin %1% %2%)' data_manager_item_buttons: '[Просмотр:](gray) [[🪣 Инвентарь…]](color=#a17b5f-#f5b98c show_text=&7Нажмите для просмотра run_command=/inventory %1% %2%) [[⌀ Эндер-сундук…]](#b649c4-#d254ff show_text=&7Нажмите для просмотра run_command=/enderchest %1% %2%)'
data_manager_system_buttons: '[Система:](gray) [[⏷ Дамп в файл…]](dark_gray show_text=&7Нажмите для дампа исходного снимка данных пользователя в файл\n&8Файлы дампов данных находятся в ~/plugins/HuskSync/dumps/ run_command=/husksync:userdata dump %1% %2% file) [[ Выгрузить дамп…]](dark_gray show_text=&7Нажмите для дампа исходного снимка данных пользователя с помощью сервиса mc-logs\n&8Вы получите ссылку с дампом данных. run_command=/husksync:userdata dump %1% %2% web)' data_manager_management_buttons: '[Управление:](gray) [[❌ Удалить…]](#ff3300 show_text=&7Нажмите для удаления этого снимка данных пользователя\n&8Не влияет на текущие данные пользователя.\n&#ff3300&⚠ Необратимое действие! suggest_command=/husksync:userdata delete %1% %2%) [[ Восстановить…]](#00fb9a show_text=&7Нажмите для восстановления данных пользователя\n&8Это восстановит данные пользователя до этого снимка.\n&#ff3300&⚠ Текущие данные %1% будут перезаписаны! suggest_command=/husksync:userdata restore %1% %2%) [[※ Закрепить/Открепить…]](#d8ff2b show_text=&7Нажмите для закрепления или открепления снимка данных пользователя\n&8Закрепленные снимки данных не удаляются автоматически. run_command=/userdata pin %1% %2%)'
data_manager_advancements_preview_remaining: 'и еще %1%…' data_manager_system_buttons: '[Система:](gray) [[⏷ Дамп в файл…]](dark_gray show_text=&7Нажмите для дампа исходного снимка данных пользователя в файл\n&8Файлы дампов данных находятся в ~/plugins/HuskSync/dumps/ run_command=/husksync:userdata dump %1% %2% file) [[☂ Выгрузить дамп…]](dark_gray show_text=&7Нажмите для дампа исходного снимка данных пользователя с помощью сервиса mc-logs\n&8Вы получите ссылку с дампом данных. run_command=/husksync:userdata dump %1% %2% web)'
data_list_title: '[Снимки данных %1%:](#00fb9a) [(%2%-%3% из](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n' data_manager_advancements_preview_remaining: 'и еще %1%…'
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_title: '[Снимки данных %1%:](#00fb9a) [(%2%-%3% из](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n'
data_deleted: '[❌ Снимок данных](#00fb9a) [%1%](#00fb9a show_text=&7UUID снимка:\n&8%2%) [пользователя](#00fb9a) [%3%](#00fb9a show_text=&7UUID игрока:\n&8%4%) [удален.](#00fb9a)' 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_restored: '[⏪ Данные пользователя](#00fb9a) [%1%](#00fb9a show_text=&7UUID игрока:\n&8%2%) [из снимка](#00fb9a) [%3%](#00fb9a show_text=&7UUID снимка:\n&8%4%) [успешно восстановлены.](#00fb9a)' data_deleted: '[❌ Снимок данных](#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)' data_restored: '[⏪ Данные пользователя](#00fb9a) [%1%](#00fb9a show_text=&7UUID игрока:\n&8%2%) [из снимка](#00fb9a) [%3%](#00fb9a show_text=&7UUID снимка:\n&8%4%) [успешно восстановлены.](#00fb9a)'
data_unpinned: '[※ Снимок данных](#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)'
data_dumped: '[☂ Дамп снимка данных %1% пользователя %2% успешно выгружен:](#00fb9a) &7%3%' data_unpinned: '[Снимок данных](#00fb9a) [%1%](#00fb9a show_text=&7UUID снимка:\n&8%2%) [пользователя](#00fb9a) [%3%](#00fb9a show_text=&7UUID пользователя:\n&8%4%) [успешно откреплен.](#00fb9a)'
list_footer: '\n%1%[Страница](#00fb9a) [%2%](#00fb9a)/[%3%](#00fb9a)%4% %5%\n' data_dumped: '[☂ Дамп снимка данных %1% пользователя %2% успешно выгружен:](#00fb9a) &7%3%'
list_previous_page_button: '[◀](white show_text=&7Просмотр предыдущей страницы run_command=%2% %1%) ' list_footer: '\n%1%[Страница](#00fb9a) [%2%](#00fb9a)/[%3%](#00fb9a)%4% %5%\n'
list_next_page_button: ' [▶](white show_text=&7Просмотр следующей страницы run_command=%2% %1%)' list_previous_page_button: '[◀](white show_text=&7Просмотр предыдущей страницы run_command=%2% %1%) '
list_page_jumpers: '(%1%)' list_next_page_button: ' [▶](white show_text=&7Просмотр следующей страницы run_command=%2% %1%)'
list_page_jumper_button: '[%1%](show_text=&7Перейти на страницу %1% run_command=%2% %1%)' list_page_jumpers: '(%1%)'
list_page_jumper_current_page: '[%1%](#00fb9a)' list_page_jumper_button: '[%1%](show_text=&7Перейти на страницу %1% run_command=%2% %1%)'
list_page_jumper_separator: ' ' list_page_jumper_current_page: '[%1%](#00fb9a)'
list_page_jumper_group_separator: '' list_page_jumper_separator: ' '
save_cause_disconnect: 'отключение с сервера' list_page_jumper_group_separator: '…'
save_cause_world_save: 'сохранение мира' save_cause_disconnect: 'отключение с сервера'
save_cause_death: 'смерть' save_cause_world_save: 'сохранение мира'
save_cause_server_shutdown: 'отключение сервера' save_cause_death: 'смерть'
save_cause_inventory_command: 'команда inventory' save_cause_server_shutdown: 'отключение сервера'
save_cause_enderchest_command: 'команда enderchest' save_cause_inventory_command: 'команда inventory'
save_cause_backup_restore: 'восстановление из снимка' save_cause_enderchest_command: 'команда enderchest'
save_cause_api: 'API' save_cause_backup_restore: 'восстановление из снимка'
save_cause_mpdb_migration: 'миграция из MPDB' save_cause_api: 'API'
save_cause_legacy_migration: 'миграция с legacy' save_cause_mpdb_migration: 'миграция из MPDB'
save_cause_converted_from_v2: 'конвертация с v2' save_cause_legacy_migration: 'миграция с legacy'
up_to_date: '[HuskSync](#00fb9a bold) [| Вы используете последнюю версию HuskSync (v%1%).](#00fb9a)' save_cause_converted_from_v2: 'конвертация с v2'
update_available: '[HuskSync](#ff7e5e bold) [| Доступна новая версия HuskSync: v%1% (текущая: v%2%).](#ff7e5e)' up_to_date: '[HuskSync](#00fb9a bold) [| Вы используете последнюю версию HuskSync (v%1%).](#00fb9a)'
reload_complete: '[HuskSync](#00fb9a bold) [| Конфигурация и файлы локализации перезагружены.](#00fb9a)\n[⚠ Убедитесь, что файлы конфигурации обновлены на всех серверах!](#00fb9a)\n[Необходима перезагрузка для вступления изменений конфигурации в силу.](#00fb9a italic)' update_available: '[HuskSync](#ff7e5e bold) [| Доступна новая версия HuskSync: v%1% (текущая: v%2%).](#ff7e5e)'
system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)' reload_complete: '[HuskSync](#00fb9a bold) [| Конфигурация и файлы локализации перезагружены.](#00fb9a)\n[⚠ Убедитесь, что файлы конфигурации обновлены на всех серверах!](#00fb9a)\n[Необходима перезагрузка для вступления изменений конфигурации в силу.](#00fb9a italic)'
error_invalid_syntax: '[Ошибка:](#ff3300) [Неправильный синтаксис. Используйте:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)' system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)'
error_invalid_player: '[Ошибка:](#ff3300) [Не удалось найти игрока с данным именем.](#ff7e5e)' error_invalid_syntax: '[Ошибка:](#ff3300) [Неправильный синтаксис. Используйте:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)'
error_no_permission: '[Ошибка:](#ff3300) [У вас недостаточно прав для выполнения данной команды.](#ff7e5e)' error_invalid_player: '[Ошибка:](#ff3300) [Не удалось найти игрока с данным именем.](#ff7e5e)'
error_console_command_only: '[Ошибка:](#ff3300) [Данная команда может быть выполнена только из консоли.](#ff7e5e)' error_no_permission: '[Ошибка:](#ff3300) [У вас недостаточно прав для выполнения данной команды.](#ff7e5e)'
error_in_game_command_only: 'Ошибка: Данная команда может быть выполнена только в игре.' error_console_command_only: '[Ошибка:](#ff3300) [Данная команда может быть выполнена только из консоли.](#ff7e5e)'
error_no_data_to_display: '[Ошибка:](#ff3300) [Не удалось найти никаких пользовательских данных для отображения.](#ff7e5e)' error_in_game_command_only: 'Ошибка: Данная команда может быть выполнена только в игре.'
error_invalid_version_uuid: '[Ошибка:](#ff3300) [Не удалось найти никаких пользовательских данных с этим UUID снимка.](#ff7e5e)' error_no_data_to_display: '[Ошибка:](#ff3300) [Не удалось найти никаких пользовательских данных для отображения.](#ff7e5e)'
husksync_command_description: 'Управление плагином HuskSync' error_invalid_version_uuid: '[Ошибка:](#ff3300) [Не удалось найти никаких пользовательских данных с этим UUID снимка.](#ff7e5e)'
userdata_command_description: 'Просмотр, редактирование и восстановление пользовательских данных игрока' husksync_command_description: 'Управление плагином HuskSync'
inventory_command_description: 'Просмотр и редактирование инвентаря игрока' userdata_command_description: 'Просмотр, редактирование и восстановление пользовательских данных игрока'
enderchest_command_description: 'Просмотр и редактирование эндер-сундука игрока' inventory_command_description: 'Просмотр и редактирование инвентаря игрока'
enderchest_command_description: 'Просмотр и редактирование эндер-сундука игрока'

View File

@@ -1,62 +1,63 @@
synchronization_complete: '[⏵ Veri senkronize edildi!](#00fb9a)' locales:
synchronization_failed: '[⏵ Veriler senkronize edilemedi! Lütfen bir yönetici ile iletişime geçin.](#ff7e5e)' synchronization_complete: '[⏵ Veri senkronize edildi!](#00fb9a)'
inventory_viewer_menu_title: '&0%1%''ın Envanteri' synchronization_failed: '[⏵ Veriler senkronize edilemedi! Lütfen bir yönetici ile iletişime geçin.](#ff7e5e)'
ender_chest_viewer_menu_title: '&0%1%''ın Ender Sandığı' inventory_viewer_menu_title: '&0%1%''ın Envanteri'
inventory_viewer_opened: '[Görüntülenen anlık](#00fb9a) [%1%](#00fb9a bold)[''ın envanteri ⌚ %2% tarihine kadar](#00fb9a)' ender_chest_viewer_menu_title: '&0%1%''ın Ender Sandığı'
ender_chest_viewer_opened: '[Görüntülenen anlık](#00fb9a) [%1%](#00fb9a bold)[''ın Ender Sandığı ⌚ %2% tarihine kadar](#00fb9a)' inventory_viewer_opened: '[Görüntülenen anlık](#00fb9a) [%1%](#00fb9a bold)[''ın envanteri ⌚ %2% tarihine kadar](#00fb9a)'
data_update_complete: '[🔔 Verileriniz güncellendi!](#00fb9a)' ender_chest_viewer_opened: '[Görüntülenen anlık](#00fb9a) [%1%](#00fb9a bold)[''ın Ender Sandığı ⌚ %2% tarihine kadar](#00fb9a)'
data_update_failed: '[🔔 Verileriniz güncellenirken bir hata oluştu! Lütfen bir yönetici ile iletişime geçin.](#ff7e5e)' data_update_complete: '[🔔 Verileriniz güncellendi!](#00fb9a)'
user_registration_complete: '[⭐ Kullanıcı kaydı tamamlandı!](#00fb9a)' data_update_failed: '[🔔 Verileriniz güncellenirken bir hata oluştu! Lütfen bir yönetici ile iletişime geçin.](#ff7e5e)'
data_manager_title: '[Kullanıcı veri anlık görünümü](#00fb9a) [%1%](#00fb9a show_text=&7Versiyon UUID:\n&8%2%) [için](#00fb9a) [%3%](#00fb9a bold show_text=&7Oyuncu UUID:\n&8%4%)[:](#00fb9a)' user_registration_complete: '[Kullanıcı kaydı tamamlandı!](#00fb9a)'
data_manager_timestamp: '[⌚ %1%](#ffc43b-#f5c962 show_text=&7Versiyon zaman damgası:\n&8Verinin ne zaman kaydedildiği)' data_manager_title: '[Kullanıcı veri anlık görünümü](#00fb9a) [%1%](#00fb9a show_text=&7Versiyon UUID:\n&8%2%) [için](#00fb9a) [%3%](#00fb9a bold show_text=&7Oyuncu UUID:\n&8%4%)[:](#00fb9a)'
data_manager_pinned: '[※ Anlık sabitlendi](#d8ff2b show_text=&7Sabitlendi:\n&8Bu kullanıcı veri anlığı otomatik olarak döndürülmeyecek.)' data_manager_timestamp: '[⌚ %1%](#ffc43b-#f5c962 show_text=&7Versiyon zaman damgası:\n&8Verinin ne zaman kaydedildiği)'
data_manager_cause: '[⚑ %1%](#23a825-#36f539 show_text=&7Kaydetme sebebi:\n&8Verinin kaydedilme nedeni)' data_manager_pinned: '[※ Anlık sabitlendi](#d8ff2b show_text=&7Sabitlendi:\n&8Bu kullanıcı veri anlığı otomatik olarak döndürülmeyecek.)'
data_manager_server: '[ %1%](#ff87b3-#f5538e show_text=&7Sunucu:\n&8Verinin kaydedildiği sunucu adı)' data_manager_cause: '[ %1%](#23a825-#36f539 show_text=&7Kaydetme sebebi:\n&8Verinin kaydedilme nedeni)'
data_manager_size: '[ %1%](color=#62a9f5-#7ab8fa show_text=&7Anlık boyutu:\n&8Anlının tahmini dosya boyutu (KiB cinsinden))\n' data_manager_server: '[ %1%](#ff87b3-#f5538e show_text=&7Sunucu:\n&8Verinin kaydedildiği sunucu adı)'
data_manger_status: '[%1%](red)[/](gray)[%2%](red)[×](gray)[❤](red show_text=&7Can puanı) [%3%](yellow)[×](gray)[🍖](yellow show_text=&7Açlık puanı) [ʟᴠ](green)[.](gray)[%4%](green show_text=&7XP seviyesi) [🏹 %5%](dark_aqua show_text=&7Oyun modu)' data_manager_size: '[%1%](color=#62a9f5-#7ab8fa show_text=&7Anlık boyutu:\n&8Anlının tahmini dosya boyutu (KiB cinsinden))\n'
data_manager_advancements_statistics: '[⭐ Gelişmeler: %1%](color=#ffc43b-#f5c962 show_text=&7Gelişmelerdeki ilerlemeniz:\n&8%2%) [⌛ Oynama Süresi: %3%s](color=#62a9f5-#7ab8fa show_text=&7Oyunda geçen süre\n&8⚠ Oyun içi istatistiklere dayanır)\n' data_manger_status: '[%1%](red)[/](gray)[%2%](red)[×](gray)[❤](red show_text=&7Can puanı) [%3%](yellow)[×](gray)[🍖](yellow show_text=&7Açlık puanı) [ʟᴠ](green)[.](gray)[%4%](green show_text=&7XP seviyesi) [🏹 %5%](dark_aqua show_text=&7Oyun modu)'
data_manager_item_buttons: '[Görünüm:](gray) [[🪣 Envanter…]](color=#a17b5f-#f5b98c show_text=&7Görüntülemek için tıklayın run_command=/inventory %1% %2%) [[⌀ Ender Sandığı…]](#b649c4-#d254ff show_text=&7Görüntülemek için tıklayın run_command=/enderchest %1% %2%)' data_manager_advancements_statistics: '[⭐ Gelişmeler: %1%](color=#ffc43b-#f5c962 show_text=&7Gelişmelerdeki ilerlemeniz:\n&8%2%) [⌛ Oynama Süresi: %3%s](color=#62a9f5-#7ab8fa show_text=&7Oyunda geçen süre\n&8⚠ Oyun içi istatistiklere dayanır)\n'
data_manager_management_buttons: '[Yönet:](gray) [[❌ Sil…]](#ff3300 show_text=&7Bu kullanıcı veri anlığını silmek için tıklayın.\n&8Bu, kullanıcının mevcut verilerini etkilemez.\n&#ff3300&⚠ Geri alınamaz! suggest_command=/husksync:userdata delete %1% %2%) [[⏪ Geri Yükle…]](#00fb9a show_text=&7Bu kullanıcı verisini geri yüklemek için tıklayın.\n&8Bu, kullanıcının verisini bu anlığa ayarlar.\n&#ff3300&⚠ %1%''nın mevcut verisi üzerine yazılacak! suggest_command=/husksync:userdata restore %1% %2%) [[※ Sabitle/Çöz…]](#d8ff2b show_text=&7Bu kullanıcı veri anlığını sabitlemek veya çözmek için tıklayın\n&8Sabitlenmiş anlıklar otomatik olarak döndürülmez run_command=/userdata pin %1% %2%)' data_manager_item_buttons: '[Görünüm:](gray) [[🪣 Envanter…]](color=#a17b5f-#f5b98c show_text=&7Görüntülemek için tıklayın run_command=/inventory %1% %2%) [[⌀ Ender Sandığı…]](#b649c4-#d254ff show_text=&7Görüntülemek için tıklayın run_command=/enderchest %1% %2%)'
data_manager_system_buttons: '[Sistem:](gray) [[⏷ Dosya Dök…]](dark_gray show_text=&7Bu ham kullanıcı veri anlığını bir dosyaya dökmek için tıklayın.\n&8Veri dökümleri ~/plugins/HuskSync/dumps/ klasöründe bulunabilir. run_command=/husksync:userdata dump %1% %2% file) [[☂ Web Dök…]](dark_gray show_text=&7Bu ham kullanıcı veri anlığını mc-logs servisine dökme için tıklayın\n&8Verileri içeren bir URL ile sağlanacaksınız. run_command=/husksync:userdata dump %1% %2% web)' data_manager_management_buttons: '[Yönet:](gray) [[❌ Sil…]](#ff3300 show_text=&7Bu kullanıcı veri anlığını silmek için tıklayın.\n&8Bu, kullanıcının mevcut verilerini etkilemez.\n&#ff3300&⚠ Geri alınamaz! suggest_command=/husksync:userdata delete %1% %2%) [[⏪ Geri Yükle…]](#00fb9a show_text=&7Bu kullanıcı verisini geri yüklemek için tıklayın.\n&8Bu, kullanıcının verisini bu anlığa ayarlar.\n&#ff3300&⚠ %1%''nın mevcut verisi üzerine yazılacak! suggest_command=/husksync:userdata restore %1% %2%) [[※ Sabitle/Çöz…]](#d8ff2b show_text=&7Bu kullanıcı veri anlığını sabitlemek veya çözmek için tıklayın\n&8Sabitlenmiş anlıklar otomatik olarak döndürülmez run_command=/userdata pin %1% %2%)'
data_manager_advancements_preview_remaining: 've %1% daha fazla…' data_manager_system_buttons: '[Sistem:](gray) [[⏷ Dosya Dök…]](dark_gray show_text=&7Bu ham kullanıcı veri anlığını bir dosyaya dökmek için tıklayın.\n&8Veri dökümleri ~/plugins/HuskSync/dumps/ klasöründe bulunabilir. run_command=/husksync:userdata dump %1% %2% file) [[☂ Web Dök…]](dark_gray show_text=&7Bu ham kullanıcı veri anlığını mc-logs servisine dökme için tıklayın\n&8Verileri içeren bir URL ile sağlanacaksınız. run_command=/husksync:userdata dump %1% %2% web)'
data_list_title: '[%1%''ın kullanıcı veri anlıkları:](#00fb9a) [(%2%-%3% /](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n' data_manager_advancements_preview_remaining: 've %1% daha fazla…'
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_title: '[%1%''ın kullanıcı veri anlıkları:](#00fb9a) [(%2%-%3% /](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n'
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_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_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_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_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%)' 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_unpinned: '[※ Kullanıcı veri anlığı başarıyla çözüldü](#00fb9a) [%1%](#00fb9a show_text=&7Versiyon UUID:\n&8%2%) [için](#00fb9a) [%3%.](#00fb9a show_text=&7Oyuncu 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%)'
data_dumped: '[ Kullanıcı veri anlığı başarıyla döküldü %1% için %2%:](#00fb9a) &7%3%' data_unpinned: '[ Kullanıcı veri anlığı başarıyla çözüldü](#00fb9a) [%1%](#00fb9a show_text=&7Versiyon UUID:\n&8%2%) [için](#00fb9a) [%3%.](#00fb9a show_text=&7Oyuncu UUID:\n&8%4%)'
list_footer: '\n%1%[Sayfa](#00fb9a) [%2%](#00fb9a)/[%3%](#00fb9a)%4% %5%' data_dumped: '[☂ Kullanıcı veri anlığı başarıyla döküldü %1% için %2%:](#00fb9a) &7%3%'
list_previous_page_button: '[◀](white show_text=&7Önceki sayfayı görüntüle run_command=%2% %1%) ' list_footer: '\n%1%[Sayfa](#00fb9a) [%2%](#00fb9a)/[%3%](#00fb9a)%4% %5%'
list_next_page_button: ' [▶](white show_text=&7Sonraki sayfayı görüntüle run_command=%2% %1%)' list_previous_page_button: '[◀](white show_text=&7Önceki sayfayı görüntüle run_command=%2% %1%) '
list_page_jumpers: '(%1%)' list_next_page_button: ' [▶](white show_text=&7Sonraki sayfayı görüntüle run_command=%2% %1%)'
list_page_jumper_button: '[%1%](show_text=&7Sayfaya git %1% run_command=%2% %1%)' list_page_jumpers: '(%1%)'
list_page_jumper_current_page: '[%1%](#00fb9a)' list_page_jumper_button: '[%1%](show_text=&7Sayfaya git %1% run_command=%2% %1%)'
list_page_jumper_separator: ' ' list_page_jumper_current_page: '[%1%](#00fb9a)'
list_page_jumper_group_separator: '' list_page_jumper_separator: ' '
save_cause_disconnect: 'disconnect' list_page_jumper_group_separator: '…'
save_cause_world_save: 'world save' save_cause_disconnect: 'bağlantı kesilmesi'
save_cause_death: 'death' save_cause_world_save: 'dünya kaydı'
save_cause_server_shutdown: 'server shutdown' save_cause_death: 'ölüm'
save_cause_inventory_command: 'inventory command' save_cause_server_shutdown: 'sunucu kapatma'
save_cause_enderchest_command: 'enderchest command' save_cause_inventory_command: 'envanter komutu'
save_cause_backup_restore: 'backup restore' save_cause_enderchest_command: 'ender sandığı komutu'
save_cause_api: 'API' save_cause_backup_restore: 'yedek geri yükleme'
save_cause_mpdb_migration: 'MPDB migration' save_cause_api: 'API'
save_cause_legacy_migration: 'legacy migration' save_cause_mpdb_migration: 'MPDB migration'
save_cause_converted_from_v2: 'converted from v2' save_cause_legacy_migration: 'legacy migration'
up_to_date: '[HuskSync](#00fb9a bold) [| HuskSync\''in en son sürümünü kullanıyorsunuz (v%1%).](#00fb9a)' save_cause_converted_from_v2: 'v2 den dönüştürüldü'
update_available: '[HuskSync](#ff7e5e bold) [| HuskSync\''in yeni bir sürümü mevcut: v%1% (kullanılan sürüm: v%2%).](#ff7e5e)' up_to_date: '[HuskSync](#00fb9a bold) [| HuskSync\''in en son sürümünü kullanıyorsunuz (v%1%).](#00fb9a)'
reload_complete: '[HuskSync](#00fb9a bold) [| Yapılandırma ve mesaj dosyaları yeniden yüklendi.](#00fb9a)\n[⚠ Lütfen yapılandırma dosyalarının tüm sunucularda güncel olduğundan emin olun!](#00fb9a)\n[Yapılandırma değişikliklerinin etkili olabilmesi için bir yeniden başlatma gereklidir.](#00fb9a italic)' update_available: '[HuskSync](#ff7e5e bold) [| HuskSync\''in yeni bir sürümü mevcut: v%1% (kullanılan sürüm: v%2%).](#ff7e5e)'
system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)' reload_complete: '[HuskSync](#00fb9a bold) [| Yapılandırma ve mesaj dosyaları yeniden yüklendi.](#00fb9a)\n[⚠ Lütfen yapılandırma dosyalarının tüm sunucularda güncel olduğundan emin olun!](#00fb9a)\n[Yapılandırma değişikliklerinin etkili olabilmesi için bir yeniden başlatma gereklidir.](#00fb9a italic)'
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%)' system_status_header: '[HuskSync](#00fb9a bold) [| Sistem durumu raporu:](#00fb9a)'
error_invalid_player: '[Hata:](#ff3300) [Bu isimde bir oyuncu bulunamadı.](#ff7e5e)' 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_no_permission: '[Hata:](#ff3300) [Bu komutu gerçekleştirmek için izniniz yok](#ff7e5e)' error_invalid_player: '[Hata:](#ff3300) [Bu isimde bir oyuncu bulunamadı.](#ff7e5e)'
error_console_command_only: '[Hata:](#ff3300) [Bu komut yalnızca konsoldan çalıştırılabilir](#ff7e5e)' error_no_permission: '[Hata:](#ff3300) [Bu komutu gerçekleştirmek için izniniz yok](#ff7e5e)'
error_in_game_command_only: 'Hata: Bu komut yalnızca oyun içinde kullanılabilir.' error_console_command_only: '[Hata:](#ff3300) [Bu komut yalnızca konsoldan çalıştırılabilir](#ff7e5e)'
error_no_data_to_display: '[Hata:](#ff3300) [Görüntülenecek kullanıcı verisi bulunamadı.](#ff7e5e)' error_in_game_command_only: 'Hata: Bu komut yalnızca oyun içinde kullanılabilir.'
error_invalid_version_uuid: '[Hata:](#ff3300) [Bu sürüm UUID''si için kullanıcı verisi bulunamadı.](#ff7e5e)' error_no_data_to_display: '[Hata:](#ff3300) [Görüntülenecek kullanıcı verisi bulunamadı.](#ff7e5e)'
husksync_command_description: 'HuskSync eklentisini yönet' error_invalid_version_uuid: '[Hata:](#ff3300) [Bu sürüm UUID''si için kullanıcı verisi bulunamadı.](#ff7e5e)'
userdata_command_description: 'Oyuncu verilerini görüntüle, yönet ve geri yükle' husksync_command_description: 'HuskSync eklentisini yönet'
inventory_command_description: 'Oyuncunun envanterini görüntüle ve düzenle' userdata_command_description: 'Oyuncu verilerini görüntüle, yönet ve geri yükle'
enderchest_command_description: 'Oyuncunun Ender Chest''ini görüntüle ve düzenle' inventory_command_description: 'Oyuncunun envanterini görüntüle ve düzenle'
enderchest_command_description: 'Oyuncunun Ender Chest''ini görüntüle ve düzenle'

View File

@@ -1,62 +1,63 @@
synchronization_complete: '[⏵ Дані синхронізовано!](#00fb9a)' locales:
synchronization_failed: '[⏵ Failed to synchronize your data! Please contact an administrator.](#ff7e5e)' synchronization_complete: '[⏵ Дані синхронізовано!](#00fb9a)'
inventory_viewer_menu_title: '&0%1%''s Inventory' synchronization_failed: '[⏵ Failed to synchronize your data! Please contact an administrator.](#ff7e5e)'
ender_chest_viewer_menu_title: '&0%1%''s Ender Chest' inventory_viewer_menu_title: '&0%1%''s Inventory'
inventory_viewer_opened: '[Viewing snapshot of](#00fb9a) [%1%](#00fb9a bold)[''s inventory as of ⌚ %2%](#00fb9a)' ender_chest_viewer_menu_title: '&0%1%''s Ender Chest'
ender_chest_viewer_opened: '[Viewing snapshot of](#00fb9a) [%1%](#00fb9a bold)[''s Ender Chest as of ⌚ %2%](#00fb9a)' inventory_viewer_opened: '[Viewing snapshot of](#00fb9a) [%1%](#00fb9a bold)[''s inventory as of ⌚ %2%](#00fb9a)'
data_update_complete: '[🔔 Your data has been updated!](#00fb9a)' ender_chest_viewer_opened: '[Viewing snapshot of](#00fb9a) [%1%](#00fb9a bold)[''s Ender Chest as of ⌚ %2%](#00fb9a)'
data_update_failed: '[🔔 Failed to update your data! Please contact an administrator.](#ff7e5e)' data_update_complete: '[🔔 Your data has been updated!](#00fb9a)'
user_registration_complete: '[⭐ User registration complete!](#00fb9a)' data_update_failed: '[🔔 Failed to update your data! Please contact an administrator.](#ff7e5e)'
data_manager_title: '[Viewing user data snapshot](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\n&8%2%) [for](#00fb9a) [%3%](#00fb9a bold show_text=&7Player UUID:\n&8%4%)[:](#00fb9a)' user_registration_complete: '[⭐ User registration complete!](#00fb9a)'
data_manager_timestamp: '[⌚ %1%](#ffc43b-#f5c962 show_text=&7Version timestamp:\n&8When the data was saved)' data_manager_title: '[Viewing user data snapshot](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\n&8%2%) [for](#00fb9a) [%3%](#00fb9a bold show_text=&7Player UUID:\n&8%4%)[:](#00fb9a)'
data_manager_pinned: '[※ Snapshot pinned](#d8ff2b show_text=&7Pinned:\n&8This user data snapshot won''t be automatically rotated.)' data_manager_timestamp: '[⌚ %1%](#ffc43b-#f5c962 show_text=&7Version timestamp:\n&8When the data was saved)'
data_manager_cause: '[⚑ %1%](#23a825-#36f539 show_text=&7Save cause:\n&8What caused the data to be saved)' data_manager_pinned: '[※ Snapshot pinned](#d8ff2b show_text=&7Pinned:\n&8This user data snapshot won''t be automatically rotated.)'
data_manager_server: '[ %1%](#ff87b3-#f5538e show_text=&7Server:\n&8Name of the server the data was saved on)' data_manager_cause: '[ %1%](#23a825-#36f539 show_text=&7Save cause:\n&8What caused the data to be saved)'
data_manager_size: '[ %1%](color=#62a9f5-#7ab8fa show_text=&7Snapshot size:\n&8Estimated file size of the snapshot (in KiB))\n' data_manager_server: '[ %1%](#ff87b3-#f5538e show_text=&7Server:\n&8Name of the server the data was saved on)'
data_manger_status: '[%1%](red)[/](gray)[%2%](red)[×](gray)[❤](red show_text=&7Health points) [%3%](yellow)[×](gray)[🍖](yellow show_text=&7Hunger points) [ʟᴠ](green)[.](gray)[%4%](green show_text=&7XP level) [🏹 %5%](dark_aqua show_text=&7Game mode)' data_manager_size: '[%1%](color=#62a9f5-#7ab8fa show_text=&7Snapshot size:\n&8Estimated file size of the snapshot (in KiB))\n'
data_manager_advancements_statistics: '[⭐ Advancements: %1%](color=#ffc43b-#f5c962 show_text=&7Advancements you have progress in:\n&8%2%) [⌛ Play Time: %3%ʜʀs](color=#62a9f5-#7ab8fa show_text=&7In-game play time\n&8⚠ Based on in-game statistics)\n' data_manger_status: '[%1%](red)[/](gray)[%2%](red)[×](gray)[❤](red show_text=&7Health points) [%3%](yellow)[×](gray)[🍖](yellow show_text=&7Hunger points) [ʟᴠ](green)[.](gray)[%4%](green show_text=&7XP level) [🏹 %5%](dark_aqua show_text=&7Game mode)'
data_manager_item_buttons: '[View:](gray) [[🪣 Inventory…]](color=#a17b5f-#f5b98c show_text=&7Click to view run_command=/inventory %1% %2%) [[⌀ Ender Chest…]](#b649c4-#d254ff show_text=&7Click to view run_command=/enderchest %1% %2%)' data_manager_advancements_statistics: '[⭐ Advancements: %1%](color=#ffc43b-#f5c962 show_text=&7Advancements you have progress in:\n&8%2%) [⌛ Play Time: %3%ʜʀs](color=#62a9f5-#7ab8fa show_text=&7In-game play time\n&8⚠ Based on in-game statistics)\n'
data_manager_management_buttons: '[Manage:](gray) [[❌ Delete…]](#ff3300 show_text=&7Click to delete this snapshot of user data.\n&8This will not affect the user''s current data.\n&#ff3300&⚠ This cannot be undone! suggest_command=/husksync:userdata delete %1% %2%) [[⏪ Restore…]](#00fb9a show_text=&7Click to restore this user data.\n&8This will set the user''s data to this snapshot.\n&#ff3300&⚠ %1%''s current data will be overwritten! suggest_command=/husksync:userdata restore %1% %2%) [[※ Pin/Unpin…]](#d8ff2b show_text=&7Click to pin or unpin this user data snapshot\n&8Pinned snapshots won''t be automatically rotated run_command=/userdata pin %1% %2%)' data_manager_item_buttons: '[View:](gray) [[🪣 Inventory…]](color=#a17b5f-#f5b98c show_text=&7Click to view run_command=/inventory %1% %2%) [[⌀ Ender Chest…]](#b649c4-#d254ff show_text=&7Click to view run_command=/enderchest %1% %2%)'
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_management_buttons: '[Manage:](gray) [[❌ Delete…]](#ff3300 show_text=&7Click to delete this snapshot of user data.\n&8This will not affect the user''s current data.\n&#ff3300&⚠ This cannot be undone! suggest_command=/husksync:userdata delete %1% %2%) [[⏪ Restore…]](#00fb9a show_text=&7Click to restore this user data.\n&8This will set the user''s data to this snapshot.\n&#ff3300&⚠ %1%''s current data will be overwritten! suggest_command=/husksync:userdata restore %1% %2%) [[※ Pin/Unpin…]](#d8ff2b show_text=&7Click to pin or unpin this user data snapshot\n&8Pinned snapshots won''t be automatically rotated run_command=/userdata pin %1% %2%)'
data_manager_advancements_preview_remaining: 'and %1% more…' 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_list_title: '[%1%''s user data snapshots:](#00fb9a) [(%2%-%3% of](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n' data_manager_advancements_preview_remaining: 'and %1% more…'
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_title: '[%1%''s user data snapshots:](#00fb9a) [(%2%-%3% of](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n'
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_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_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_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_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%)' 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_unpinned: '[※ Successfully unpinned 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_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%)'
data_dumped: '[ Successfully dumped the user data snapshot %1% for %2% to:](#00fb9a) &7%3%' data_unpinned: '[ Successfully unpinned user data snapshot](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\n&8%2%) [for](#00fb9a) [%3%.](#00fb9a show_text=&7Player UUID:\n&8%4%)'
list_footer: '\n%1%[Page](#00fb9a) [%2%](#00fb9a)/[%3%](#00fb9a)%4% %5%' data_dumped: '[☂ Successfully dumped the user data snapshot %1% for %2% to:](#00fb9a) &7%3%'
list_previous_page_button: '[◀](white show_text=&7View previous page run_command=%2% %1%) ' list_footer: '\n%1%[Page](#00fb9a) [%2%](#00fb9a)/[%3%](#00fb9a)%4% %5%'
list_next_page_button: ' [▶](white show_text=&7View next page run_command=%2% %1%)' list_previous_page_button: '[◀](white show_text=&7View previous page run_command=%2% %1%) '
list_page_jumpers: '(%1%)' list_next_page_button: ' [▶](white show_text=&7View next page run_command=%2% %1%)'
list_page_jumper_button: '[%1%](show_text=&7Jump to page %1% run_command=%2% %1%)' list_page_jumpers: '(%1%)'
list_page_jumper_current_page: '[%1%](#00fb9a)' list_page_jumper_button: '[%1%](show_text=&7Jump to page %1% run_command=%2% %1%)'
list_page_jumper_separator: ' ' list_page_jumper_current_page: '[%1%](#00fb9a)'
list_page_jumper_group_separator: '' list_page_jumper_separator: ' '
save_cause_disconnect: 'disconnect' list_page_jumper_group_separator: '…'
save_cause_world_save: 'world save' save_cause_disconnect: 'disconnect'
save_cause_death: 'death' save_cause_world_save: 'world save'
save_cause_server_shutdown: 'server shutdown' save_cause_death: 'death'
save_cause_inventory_command: 'inventory command' save_cause_server_shutdown: 'server shutdown'
save_cause_enderchest_command: 'enderchest command' save_cause_inventory_command: 'inventory command'
save_cause_backup_restore: 'backup restore' save_cause_enderchest_command: 'enderchest command'
save_cause_api: 'API' save_cause_backup_restore: 'backup restore'
save_cause_mpdb_migration: 'MPDB migration' save_cause_api: 'API'
save_cause_legacy_migration: 'legacy migration' save_cause_mpdb_migration: 'MPDB migration'
save_cause_converted_from_v2: 'converted from v2' save_cause_legacy_migration: 'legacy migration'
up_to_date: '[HuskSync](#00fb9a bold) [| You are running the latest version of HuskSync (v%1%).](#00fb9a)' save_cause_converted_from_v2: 'converted from v2'
update_available: '[HuskSync](#ff7e5e bold) [| A new version of HuskSync is available: v%1% (running: v%2%).](#ff7e5e)' up_to_date: '[HuskSync](#00fb9a bold) [| You are running the latest version of HuskSync (v%1%).](#00fb9a)'
reload_complete: '[HuskSync](#00fb9a bold) [| Перезавантажено конфіґ та файли повідомлень.](#00fb9a)\n[⚠ Ensure config files are up-to-date on all servers!](#00fb9a)\n[A restart is needed for config changes to take effect.](#00fb9a italic)' update_available: '[HuskSync](#ff7e5e bold) [| A new version of HuskSync is available: v%1% (running: v%2%).](#ff7e5e)'
system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)' reload_complete: '[HuskSync](#00fb9a bold) [| Перезавантажено конфіґ та файли повідомлень.](#00fb9a)\n[⚠ Ensure config files are up-to-date on all servers!](#00fb9a)\n[A restart is needed for config changes to take effect.](#00fb9a italic)'
error_invalid_syntax: '[Помилка:](#ff3300) [Неправильний синтакс. Використання:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)' system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)'
error_invalid_player: '[Помилка:](#ff3300) [Гравця не знайдено](#ff7e5e)' error_invalid_syntax: '[Помилка:](#ff3300) [Неправильний синтакс. Використання:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)'
error_no_permission: '[Помилка:](#ff3300) [Ввас немає дозволу на використання цієї команди](#ff7e5e)' error_invalid_player: '[Помилка:](#ff3300) [Гравця не знайдено](#ff7e5e)'
error_console_command_only: '[Помилка:](#ff3300) [Ця команда може бути використана лише з допомогою %1% консолі](#ff7e5e)' error_no_permission: '[Помилка:](#ff3300) [Ввас немає дозволу на використання цієї команди](#ff7e5e)'
error_in_game_command_only: 'Error: That command can only be used in-game.' error_console_command_only: '[Помилка:](#ff3300) [Ця команда може бути використана лише з допомогою %1% консолі](#ff7e5e)'
error_no_data_to_display: '[Error:](#ff3300) [Could not find any user data to display.](#ff7e5e)' error_in_game_command_only: 'Error: That command can only be used in-game.'
error_invalid_version_uuid: '[Error:](#ff3300) [Could not find any user data for that version UUID.](#ff7e5e)' error_no_data_to_display: '[Error:](#ff3300) [Could not find any user data to display.](#ff7e5e)'
husksync_command_description: 'Manage the HuskSync plugin' error_invalid_version_uuid: '[Error:](#ff3300) [Could not find any user data for that version UUID.](#ff7e5e)'
userdata_command_description: 'View, manage & restore player userdata' husksync_command_description: 'Manage the HuskSync plugin'
inventory_command_description: 'View & edit a player''s inventory' userdata_command_description: 'View, manage & restore player userdata'
enderchest_command_description: 'View & edit a player''s Ender Chest' inventory_command_description: 'View & edit a player''s inventory'
enderchest_command_description: 'View & edit a player''s Ender Chest'

View File

@@ -1,62 +1,63 @@
synchronization_complete: '[⏵ 数据同步完成!](#00fb9a)' locales:
synchronization_failed: '[⏵ 无法同步数据! 请联系管理员.](#ff7e5e)' synchronization_complete: '[⏵ 数据同步完成!](#00fb9a)'
inventory_viewer_menu_title: '&0%1% 的背包' synchronization_failed: '[⏵ 无法同步你的数据! 请联系管理员.](#ff7e5e)'
ender_chest_viewer_menu_title: '&0%1% 的末影箱' inventory_viewer_menu_title: '&0%1% 的背包'
inventory_viewer_opened: '[正在查看玩家](#00fb9a) [%1%](#00fb9a bold) [于 ⌚ %2% 的背包备份](#00fb9a)' ender_chest_viewer_menu_title: '&0%1% 的末影箱'
ender_chest_viewer_opened: '[正在查看玩家](#00fb9a) [%1%](#00fb9a bold) [于 ⌚ %2% 的末影箱备份](#00fb9a)' inventory_viewer_opened: '[查看备份](#00fb9a) [%1%](#00fb9a bold) [于 ⌚ %2% 的背包备份](#00fb9a)'
data_update_complete: '[🔔 你的用户数据已更新!](#00fb9a)' ender_chest_viewer_opened: '[查看备份](#00fb9a) [%1%](#00fb9a bold) [于 ⌚ %2% 的末影箱备份](#00fb9a)'
data_update_failed: '[🔔 无法更新你的用户数据! 请联系管理员.](#ff7e5e)' data_update_complete: '[🔔 你的数据已更新!](#00fb9a)'
user_registration_complete: '[⭐ 用户注册完成!](#00fb9a)' data_update_failed: '[🔔 无法更新你的数据! 请联系管理员.](#ff7e5e)'
data_manager_title: '[正在查看玩家](#00fb9a) [%3%](#00fb9a bold show_text=&7玩家 UUID:\n&8%4%) [的数据备份](#00fb9a) [%1%](#00fb9a show_text=&7备份版本 UUID:\n&8%2%) [:](#00fb9a)' user_registration_complete: '[⭐ 用户注册完成!](#00fb9a)'
data_manager_timestamp: '[⌚ %1%](#ffc43b-#f5c962 show_text=&7备份时间:\n&7何时保存了此数据)' data_manager_title: '[正在查看玩家](#00fb9a) [%3%](#00fb9a bold show_text=&7玩家UUID:\n&8%4%) [的数据备份](#00fb9a) [%1%](#00fb9a show_text=&7备份版本UUID:\n&8%2%)[:](#00fb9a)'
data_manager_pinned: '[※ 置顶备份](#d8ff2b show_text=&7置顶:\n&8数据备份不会按照备份时间自动排序.)' data_manager_timestamp: '[⌚ %1%](#ffc43b-#f5c962 show_text=&7备份时间:\n&8数据保存时间)'
data_manager_cause: '[⚑ %1%](#23a825-#36f539 show_text=&7备份原因:\n&7为何保存了此数据)' data_manager_pinned: '[※ 置顶备份](#d8ff2b show_text=&7已置顶:\n&8此玩家数据备份不会按照备份时间自动排序.)'
data_manager_server: '[ %1%](#ff87b3-#f5538e show_text=&7服务器:\n&8保存数据的服务器的名称)' data_manager_cause: '[ %1%](#23a825-#36f539 show_text=&7保存原因:\n&8导致数据保存的原因)'
data_manager_size: '[ %1%](color=#62a9f5-#7ab8fa show_text=&7快照大小:\n&8快照的估计文件大小以KiB为单位)\n' data_manager_server: '[ %1%](#ff87b3-#f5538e show_text=&7服务器:\n&8数据保存所在服务器的名称)'
data_manger_status: '[%1%](red)[/](gray)[%2%](red)[×](gray)[❤](red show_text=&7血量) [%3%](yellow)[×](gray)[🍖](yellow show_text=&7饱食度) [ʟᴠ](green)[.](gray)[%4%](green show_text=&7经验等级) [🏹 %5%](dark_aqua show_text=&7游戏模式)' data_manager_size: '[%1%](color=#62a9f5-#7ab8fa show_text=&7备份大小:\n&8预计备份的文件大小(以KiB为单位))\n'
data_manager_advancements_statistics: '[⭐ 成就: %1%](color=#ffc43b-#f5c962 show_text=&7%2%) [⌛ 游玩时间: %3%ʜʀs](color=#62a9f5-#7ab8fa show_text=&7⚠ 基于游戏内的统计)\n' data_manger_status: '[%1%](red)[/](gray)[%2%](red)[×](gray)[❤](red show_text=&7生命值) [%3%](yellow)[×](gray)[🍖](yellow show_text=&7饥饿值) [ʟᴠ](green)[.](gray)[%4%](green show_text=&7经验等级) [🏹 %5%](dark_aqua show_text=&7游戏模式)'
data_manager_item_buttons: '[View:](gray) [[🪣 背包…]](color=#a17b5f-#f5b98c show_text=&7点击查看 run_command=/inventory %1% %2%) [[⌀ 末影箱…]](#b649c4-#d254ff show_text=&7点击查看 run_command=/enderchest %1% %2%)' data_manager_advancements_statistics: '[⭐ 进度: %1%](color=#ffc43b-#f5c962 show_text=&7你已经取得的进度:\n&8%2%) [⌛ 游玩时间: %3%小时](color=#62a9f5-#7ab8fa show_text=&7在游戏内游玩的时间\n&8⚠ 基于游戏内的统计信息)\n'
data_manager_management_buttons: '[Manage:](gray) [[❌ 删除…]](#ff3300 show_text=&7点击删除此数据备份.\n这不会影响玩家当前的数据.\n&#ff3300&⚠ 此操作不可撤销! suggest_command=/husksync:userdata delete %1% %2%) [[⏪ 恢复…]](#00fb9a show_text=&7点击让玩家恢复到此数据备份.\n这将会使玩家的数据恢复到这个备份.\n&#ff3300&⚠ %1% 当前的用户数据会被备份数据所覆盖! suggest_command=/husksync:userdata restore %1% %2%) [[※ 固定/取消固定…]](#d8ff2b show_text=&7单击可固定或取消固定此用户数据快照\n&8固定的快照不会自动重载 run_command=/userdata pin %1% %2%)' data_manager_item_buttons: '[查看:](gray) [[🪣 背包…]](color=#a17b5f-#f5b98c show_text=&7点击查看 run_command=/inventory %1% %2%) [[⌀ 末影箱…]](#b649c4-#d254ff show_text=&7点击查看 run_command=/enderchest %1% %2%)'
data_manager_system_buttons: '[System:](gray) [[⏷ 文件储存…]](dark_gray show_text=&7单击将此原始用户数据快照储存到文件.\n&8数据储存可以在 ~/plugins/HuskSync/dumps/ 中找到 run_command=/husksync:userdata dump %1% %2% file) [[☂ Web 储存…]](dark_gray show_text=&7单击将此原始用户数据快照转储到mc日志服务\n&8您将获得包含数据的URL. run_command=/husksync:userdata dump %1% %2% web)' data_manager_management_buttons: '[管理:](gray) [[❌ 删除…]](#ff3300 show_text=&7点击删除此玩家数据备份.\n&8这不会影响用户的当前数据.\n&#ff3300&⚠ 此操作无法撤消! suggest_command=/husksync:userdata delete %1% %2%) [[⏪ 恢复…]](#00fb9a show_text=&7点击还原此玩家数据.\n&8这将使用户的数据恢复到此备份.\n&#ff3300&⚠ %1%当前数据将被覆盖! suggest_command=/husksync:userdata restore %1% %2%) [[※ 置顶/取消置顶…]](#d8ff2b show_text=&7点击置顶或取消置顶此玩家数据备份\n&8已置顶的备份不会按照备份时间自动排序 run_command=/userdata pin %1% %2%)'
data_manager_advancements_preview_remaining: '还有 %1% …' data_manager_system_buttons: '[系统:](gray) [[⏷ 文件转储…]](dark_gray show_text=&7点击将此原始玩家数据备份转储到文件中.\n&8数据转储可以在~/plugins/HuskSync/dumps/中找到 run_command=/husksync:userdata dump %1% %2% file) [[☂ 网络转储…]](dark_gray show_text=&7点击将此原始玩家数据备份转储到mc-logs服务中\n&8你将获得包含数据的网址. run_command=/husksync:userdata dump %1% %2% web)'
data_list_title: '[%1%的用户数据快照:](#00fb9a) [(%2%-%3% of](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n' data_manager_advancements_preview_remaining: '以及其他 %1%…'
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_title: '[%1%的玩家数据备份:](#00fb9a) [(%2%-%3% of](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n'
data_deleted: '[❌ 成功删除玩家](#00fb9a) [%3%](#00fb9a show_text=&7玩家 UUID:\n&7%4%) [的数据备份](#00fb9a) [%1%.](#00fb9a show_text=&7备份版本 UUID:\n&7%2%)' 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_restored: '[ 成功恢复玩家](#00fb9a) [%1%](#00fb9a show_text=&7玩家 UUID:\n&7%2%)[的数据备份](#00fb9a) [%3%.](#00fb9a show_text=&7备份版本 UUID:\n&7%4%)' data_deleted: '[ 成功删除玩家](#00fb9a) [%3%](#00fb9a show_text=&7玩家 UUID:\n&7%4%) [的数据备份](#00fb9a) [%1%.](#00fb9a show_text=&7备份版本UUID:\n&7%2%)'
data_pinned: '[ 成功置顶玩家](#00fb9a) [%3%](#00fb9a show_text=&7玩家 UUID:\n&8%4%) [的数据备份](#00fb9a) [%1%.](#00fb9a show_text=&7备份版本 UUID:\n&8%2%)' data_restored: '[ 成功恢复玩家](#00fb9a) [%1%](#00fb9a show_text=&7玩家 UUID:\n&7%2%)[的数据备份](#00fb9a) [%3%.](#00fb9a show_text=&7备份版本UUID:\n&7%4%)'
data_unpinned: '[※ 成功取消置顶玩家](#00fb9a) [%3%](#00fb9a show_text=&7玩家 UUID:\n&8%4%) [的数据备份](#00fb9a) [%1%.](#00fb9a show_text=&7备份版本 UUID:\n&8%2%)' data_pinned: '[※ 成功置顶玩家](#00fb9a) [%3%](#00fb9a show_text=&7玩家 UUID:\n&8%4%) [的数据备份](#00fb9a) [%1%.](#00fb9a show_text=&7备份版本UUID:\n&8%2%)'
data_dumped: '[☂ 已成功将 %1% 的用户数据快照 %2% 转储到:](#00fb9a) &7%3%' data_unpinned: '[※ 成功取消置顶玩家](#00fb9a) [%3%](#00fb9a show_text=&7玩家 UUID:\n&8%4%) [的数据备份](#00fb9a) [%1%.](#00fb9a show_text=&7备份版本UUID:\n&8%2%)'
list_footer: '\n%1%[Page](#00fb9a) [%2%](#00fb9a)/[%3%](#00fb9a)%4% %5%' data_dumped: '[☂ 已成功将 %1% 的玩家数据快照 %2% 转储到:](#00fb9a) &7%3%'
list_previous_page_button: '[◀](white show_text=&7查看上一页 run_command=%2% %1%) ' list_footer: '\n%1%[页数](#00fb9a) [%2%](#00fb9a)/[%3%](#00fb9a)%4% %5%'
list_next_page_button: ' [▶](white show_text=&7查看一页 run_command=%2% %1%)' list_previous_page_button: '[◀](white show_text=&7查看一页 run_command=%2% %1%) '
list_page_jumpers: '(%1%)' list_next_page_button: ' [▶](white show_text=&7查看下一页 run_command=%2% %1%)'
list_page_jumper_button: '[%1%](show_text=&7跳转到页面 %1% run_command=%2% %1%)' list_page_jumpers: '(%1%)'
list_page_jumper_current_page: '[%1%](#00fb9a)' list_page_jumper_button: '[%1%](show_text=&7跳转至第 %1% 页 run_command=%2% %1%)'
list_page_jumper_separator: ' ' list_page_jumper_current_page: '[%1%](#00fb9a)'
list_page_jumper_group_separator: '' list_page_jumper_separator: ' '
save_cause_disconnect: '断开连接' list_page_jumper_group_separator: ''
save_cause_world_save: '保存世界' save_cause_disconnect: '断开连接'
save_cause_death: '死亡' save_cause_world_save: '保存世界'
save_cause_server_shutdown: '服务器关闭' save_cause_death: '死亡'
save_cause_inventory_command: '背包命令' save_cause_server_shutdown: '服务器关闭'
save_cause_enderchest_command: '末影箱命令' save_cause_inventory_command: '背包命令'
save_cause_backup_restore: '备份还原' save_cause_enderchest_command: '末影箱命令'
save_cause_api: 'API' save_cause_backup_restore: '备份还原'
save_cause_mpdb_migration: 'MPDB迁移' save_cause_api: 'API'
save_cause_legacy_migration: '旧版迁移' save_cause_mpdb_migration: 'MPDB迁移'
save_cause_converted_from_v2: '从v2转换' save_cause_legacy_migration: '旧版迁移'
up_to_date: '[HuskSync](#00fb9a bold) [| 你正在使用最新版本的HuskSync (v%1%)](#00fb9a)' save_cause_converted_from_v2: '从v2转换'
update_available: '[HuskSync](#ff7e5e bold) [| 一个新版本的HuskSync已经可以更新: v%1% (当前: v%2%)](#ff7e5e)' up_to_date: '[HuskSync](#00fb9a bold) [| 你正在运行最新版本的HuskSync(v%1%).](#00fb9a)'
reload_complete: '[HuskSync](#00fb9a bold) [| 插件配置和语言文件已重载.](#00fb9a)\n[⚠ 确保所有服务器上的配置文件都是最新的!](#00fb9a)\n[需要重新启动配置更改才能生效.](#00fb9a italic)' update_available: '[HuskSync](#ff7e5e bold) [| 检测到HuskSync有新版本可以更新了:v%1%(当前版本:v%2%).](#ff7e5e)'
system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)' reload_complete: '[HuskSync](#00fb9a bold) [| 重新加载配置和消息文件完成.](#00fb9a)\n[⚠ 确保在所有服务器上更新配置文件!](#00fb9a)\n[需要重新启动才能使配置更改生效.](#00fb9a italic)'
error_invalid_syntax: ':](#ff3300) [格式错误, 使用方法:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&点击建议 suggest_command=%1%)' system_status_header: '[HuskSync](#00fb9a bold) [| 系统状态报告:](#00fb9a)'
error_invalid_player: '[错误:](#ff3300) [无法找到目标玩家.](#ff7e5e)' error_invalid_syntax: '[错误:](#ff3300) [语法错误.用法:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&点击建议 suggest_command=%1%)'
error_no_permission: '[错误:](#ff3300) [你没有执行此指令的权限](#ff7e5e)' error_invalid_player: '[错误:](#ff3300) [找不到这个名称的玩家.](#ff7e5e)'
error_console_command_only: '[错误:](#ff3300) [该指令只能在控制台运行](#ff7e5e)' error_no_permission: '[错误:](#ff3300) [你没有执行此命令的权限](#ff7e5e)'
error_in_game_command_only: 'Error: 该指令只能在游戏内运行.' error_console_command_only: '[错误:](#ff3300) [该命令只能在控制台中运行](#ff7e5e)'
error_no_data_to_display: '[错误:](#ff3300) [无法找到用户数据显示.](#ff7e5e)' error_in_game_command_only: '错误: 该命令只能在游戏中使用.'
error_invalid_version_uuid: '[错误:](#ff3300) [无法找到该备份的 UUID .](#ff7e5e)' error_no_data_to_display: '[错误:](#ff3300) [找不到要显示的任何玩家数据.](#ff7e5e)'
husksync_command_description: '管理HuskSync插件' error_invalid_version_uuid: '[错误:](#ff3300) [找不到该版本UUID的任何玩家数据.](#ff7e5e)'
userdata_command_description: '查看、管理和恢复玩家用户数据' husksync_command_description: '管理HuskSync插件'
inventory_command_description: '查看和编辑玩家的背包' userdata_command_description: '查看、管理和还原玩家玩家数据'
enderchest_command_description: '查看和编辑玩家的末影箱' inventory_command_description: '查看和编辑玩家的背包'
enderchest_command_description: '查看和编辑玩家的末影箱'

View File

@@ -1,62 +1,63 @@
synchronization_complete: '[⏵資料已同步!](#00fb9a)' locales:
synchronization_failed: '[⏵ 無法同步您的資料! 請聯繫管理員](#ff7e5e)' synchronization_complete: '[⏵資料已同步!](#00fb9a)'
inventory_viewer_menu_title: '&0%1% 的背包' synchronization_failed: '[⏵ 無法同步您的資料! 請聯繫管理員](#ff7e5e)'
ender_chest_viewer_menu_title: '&0%1% 的終界箱' inventory_viewer_menu_title: '&0%1% 的背包'
inventory_viewer_opened: '[查看](#00fb9a) [%1%](#00fb9a bold) [於 ⌚ %2% 的背包快照資料](#00fb9a)' ender_chest_viewer_menu_title: '&0%1% 的終界箱'
ender_chest_viewer_opened: '[查看](#00fb9a) [%1%](#00fb9a bold) [於 ⌚ %2% 的終界箱快照資料](#00fb9a)' inventory_viewer_opened: '[查看](#00fb9a) [%1%](#00fb9a bold) [於 ⌚ %2% 的背包快照資料](#00fb9a)'
data_update_complete: '[🔔 你的資料已更新!](#00fb9a)' ender_chest_viewer_opened: '[查看](#00fb9a) [%1%](#00fb9a bold) [於 ⌚ %2% 的終界箱快照資料](#00fb9a)'
data_update_failed: '[🔔 無法更新您的資料! 請聯繫管理員](#ff7e5e)' data_update_complete: '[🔔 你的資料已更新!](#00fb9a)'
user_registration_complete: '[⭐ User registration complete!](#00fb9a)' data_update_failed: '[🔔 無法更新您的資料! 請聯繫管理員](#ff7e5e)'
data_manager_title: '[查看](#00fb9a) [%3%](#00fb9a bold show_text=&7玩家 UUID:\n&8%4%) [的快照:](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\n&8%2%) [:](#00fb9a)' user_registration_complete: '[⭐ User registration complete!](#00fb9a)'
data_manager_timestamp: '[⌚ %1%](#ffc43b-#f5c962 show_text=&7快照時間:\n&8何時保存的資料)' data_manager_title: '[查看](#00fb9a) [%3%](#00fb9a bold show_text=&7玩家 UUID:\n&8%4%) [的快照:](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\n&8%2%) [:](#00fb9a)'
data_manager_pinned: '[※ 被標記的快照](#d8ff2b show_text=&7標記:\n&8此快照資料不會自動輪換更新)' data_manager_timestamp: '[⌚ %1%](#ffc43b-#f5c962 show_text=&7快照時間:\n&8何時保存的資料)'
data_manager_cause: '[⚑ %1%](#23a825-#36f539 show_text=&7保存原因:\n&8保存此快照的原因)' data_manager_pinned: '[※ 被標記的快照](#d8ff2b show_text=&7標記:\n&8此快照資料不會自動輪換更新)'
data_manager_server: '[ %1%](#ff87b3-#f5538e show_text=&7Server:\n&8Name of the server the data was saved on)' data_manager_cause: '[ %1%](#23a825-#36f539 show_text=&7保存原因:\n&8保存此快照的原因)'
data_manager_size: '[ %1%](color=#62a9f5-#7ab8fa show_text=&7Snapshot size:\n&8Estimated file size of the snapshot (in KiB))\n' data_manager_server: '[ %1%](#ff87b3-#f5538e show_text=&7Server:\n&8Name of the server the data was saved on)'
data_manger_status: '[%1%](red)[/](gray)[%2%](red)[×](gray)[❤](red show_text=&7血量) [%3%](yellow)[×](gray)[🍖](yellow show_text=&7飽食度) [ʟᴠ](green)[.](gray)[%4%](green show_text=&7經驗等級) [🏹 %5%](dark_aqua show_text=&7遊戲模式)' data_manager_size: '[%1%](color=#62a9f5-#7ab8fa show_text=&7Snapshot size:\n&8Estimated file size of the snapshot (in KiB))\n'
data_manager_advancements_statistics: '[⭐ 成就: %1%](color=#ffc43b-#f5c962 show_text=&7已獲得的成就:\n&8%2%) [⌛ 遊戲時間: %3%ʜʀs](color=#62a9f5-#7ab8fa show_text=&7遊戲內的遊玩時間\n&8⚠ 根據遊戲內統計)\n' data_manger_status: '[%1%](red)[/](gray)[%2%](red)[×](gray)[❤](red show_text=&7血量) [%3%](yellow)[×](gray)[🍖](yellow show_text=&7飽食度) [ʟᴠ](green)[.](gray)[%4%](green show_text=&7經驗等級) [🏹 %5%](dark_aqua show_text=&7遊戲模式)'
data_manager_item_buttons: '[查看:](gray) [[🪣 背包…]](color=#a17b5f-#f5b98c show_text=&7點擊查看 run_command=/inventory %1% %2%) [[⌀ 終界箱…]](#b649c4-#d254ff show_text=&7點擊查看 run_command=/enderchest %1% %2%)' data_manager_advancements_statistics: '[⭐ 成就: %1%](color=#ffc43b-#f5c962 show_text=&7已獲得的成就:\n&8%2%) [⌛ 遊戲時間: %3%ʜʀs](color=#62a9f5-#7ab8fa show_text=&7遊戲內的遊玩時間\n&8⚠ 根據遊戲內統計)\n'
data_manager_management_buttons: '[管理:](gray) [[❌ 刪除…]](#ff3300 show_text=&7點擊刪除這個快照\n&8這不會影像目前玩家的資料\n&#ff3300&⚠ 此操作不能取消! suggest_command=/husksync:userdata delete %1% %2%) [[⏪ 恢復…]](#00fb9a show_text=&7點擊將玩家資料覆蓋為此快照\n&8這將導致玩家的資料會被此快照覆蓋\n&#ff3300&⚠ %1% 當前的資料將被覆蓋! suggest_command=/husksync:userdata restore %1% %2%) [[※ 標記…]](#d8ff2b show_text=&7點擊切換標記狀態\n&8被標記的快照將不會自動輪換更新 run_command=/userdata pin %1% %2%)' data_manager_item_buttons: '[查看:](gray) [[🪣 背包…]](color=#a17b5f-#f5b98c show_text=&7點擊查看 run_command=/inventory %1% %2%) [[⌀ 終界箱…]](#b649c4-#d254ff show_text=&7點擊查看 run_command=/enderchest %1% %2%)'
data_manager_system_buttons: '[系統:](gray) [[⏷ 本地轉存…]](dark_gray show_text=&7點擊將此玩家資料快照轉存到本地文件中\n&8轉存的資料可以在以下路徑找到 ~/plugins/HuskSync/dumps/ run_command=/husksync:userdata dump %1% %2% file) [[☂ 雲端轉存…]](dark_gray show_text=&7點擊將玩家資料快照轉存到 mc-logs 服務\n&8您將獲得一個包含資料的 URL. run_command=/husksync:userdata dump %1% %2% web)' data_manager_management_buttons: '[管理:](gray) [[❌ 刪除…]](#ff3300 show_text=&7點擊刪除這個快照\n&8這不會影像目前玩家的資料\n&#ff3300&⚠ 此操作不能取消! suggest_command=/husksync:userdata delete %1% %2%) [[⏪ 恢復…]](#00fb9a show_text=&7點擊將玩家資料覆蓋為此快照\n&8這將導致玩家的資料會被此快照覆蓋\n&#ff3300&⚠ %1% 當前的資料將被覆蓋! suggest_command=/husksync:userdata restore %1% %2%) [[※ 標記…]](#d8ff2b show_text=&7點擊切換標記狀態\n&8被標記的快照將不會自動輪換更新 run_command=/userdata pin %1% %2%)'
data_manager_advancements_preview_remaining: '還有 %1% …' data_manager_system_buttons: '[系統:](gray) [[⏷ 本地轉存…]](dark_gray show_text=&7點擊將此玩家資料快照轉存到本地文件中\n&8轉存的資料可以在以下路徑找到 ~/plugins/HuskSync/dumps/ run_command=/husksync:userdata dump %1% %2% file) [[☂ 雲端轉存…]](dark_gray show_text=&7點擊將此玩家資料快照轉存到 mc-logs 服務\n&8您將獲得一個包含資料的 URL. run_command=/husksync:userdata dump %1% %2% web)'
data_list_title: '[%1% 的玩家資料快照:](#00fb9a) [(%2%-%3% 共](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n' data_manager_advancements_preview_remaining: '還有 %1% '
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_title: '[%1% 的玩家資料快照:](#00fb9a) [(%2%-%3% 共](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n'
data_deleted: '[❌ 成功刪除:](#00fb9a) [%3%](#00fb9a show_text=&7玩家 UUID:\n&8%4%) [的快照:](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\n&8%2%)' 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_restored: '[ 成功將玩家](#00fb9a) [%1%](#00fb9a show_text=&7玩家 UUID:\n&8%2%)[的資料恢復為 快照:](#00fb9a) [%3%.](#00fb9a show_text=&7Version UUID:\n&8%4%)' data_deleted: '[ 成功刪除:](#00fb9a) [%3%](#00fb9a show_text=&7玩家 UUID:\n&8%4%) [的快照:](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\n&8%2%)'
data_pinned: '[ 成功標記](#00fb9a) [%3%](#00fb9a show_text=&7玩家 UUID:\n&8%4%) [的快照:](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\n&8%2%)' data_restored: '[ 成功將玩家](#00fb9a) [%1%](#00fb9a show_text=&7玩家 UUID:\n&8%2%)[的資料恢復為 快照:](#00fb9a) [%3%.](#00fb9a show_text=&7Version UUID:\n&8%4%)'
data_unpinned: '[※ 成功解除](#00fb9a) [%3%](#00fb9a show_text=&7玩家 UUID:\n&8%4%) [快照:](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\n&8%2%) [的標記](#00fb9a)' data_pinned: '[※ 成功標記](#00fb9a) [%3%](#00fb9a show_text=&7玩家 UUID:\n&8%4%) [快照:](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\n&8%2%)'
data_dumped: '[ 成功將 %2% 資料快照 %1% 儲存至:](#00fb9a) &7%3%' data_unpinned: '[ 成功解除](#00fb9a) [%3%](#00fb9a show_text=&7玩家 UUID:\n&8%4%) [快照:](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\n&8%2%) [的標記](#00fb9a)'
list_footer: '\n%1%[頁面](#00fb9a) [%2%](#00fb9a)/[%3%](#00fb9a)%4% %5%' data_dumped: '[☂ 成功將 %2% 資料快照 %1% 儲存至:](#00fb9a) &7%3%'
list_previous_page_button: '[◀](white show_text=&7查看上一頁 run_command=%2% %1%) ' list_footer: '\n%1%[頁面](#00fb9a) [%2%](#00fb9a)/[%3%](#00fb9a)%4% %5%'
list_next_page_button: ' [▶](white show_text=&7查看一頁 run_command=%2% %1%)' list_previous_page_button: '[◀](white show_text=&7查看一頁 run_command=%2% %1%) '
list_page_jumpers: '(%1%)' list_next_page_button: ' [▶](white show_text=&7查看下一頁 run_command=%2% %1%)'
list_page_jumper_button: '[%1%](show_text=&7跳至第 %1% 頁 run_command=%2% %1%)' list_page_jumpers: '(%1%)'
list_page_jumper_current_page: '[%1%](#00fb9a)' list_page_jumper_button: '[%1%](show_text=&7跳至第 %1% 頁 run_command=%2% %1%)'
list_page_jumper_separator: ' ' list_page_jumper_current_page: '[%1%](#00fb9a)'
list_page_jumper_group_separator: '' list_page_jumper_separator: ' '
save_cause_disconnect: 'disconnect' list_page_jumper_group_separator: '…'
save_cause_world_save: 'world save' save_cause_disconnect: 'disconnect'
save_cause_death: 'death' save_cause_world_save: 'world save'
save_cause_server_shutdown: 'server shutdown' save_cause_death: 'death'
save_cause_inventory_command: 'inventory command' save_cause_server_shutdown: 'server shutdown'
save_cause_enderchest_command: 'enderchest command' save_cause_inventory_command: 'inventory command'
save_cause_backup_restore: 'backup restore' save_cause_enderchest_command: 'enderchest command'
save_cause_api: 'API' save_cause_backup_restore: 'backup restore'
save_cause_mpdb_migration: 'MPDB migration' save_cause_api: 'API'
save_cause_legacy_migration: 'legacy migration' save_cause_mpdb_migration: 'MPDB migration'
save_cause_converted_from_v2: 'converted from v2' save_cause_legacy_migration: 'legacy migration'
up_to_date: '[HuskSync](#00fb9a bold) [| 您運行的是最新版本的 HuskSync (v%1%).](#00fb9a)' save_cause_converted_from_v2: 'converted from v2'
update_available: '[HuskSync](#ff7e5e bold) [| 發現可用的新版本: v%1% (running: v%2%).](#ff7e5e)' up_to_date: '[HuskSync](#00fb9a bold) [| 您運行的是最新版本的 HuskSync (v%1%).](#00fb9a)'
reload_complete: '[HuskSync](#00fb9a bold) [| 已重新載入配置和訊息文件](#00fb9a)\n[⚠ Ensure config files are up-to-date on all servers!](#00fb9a)\n[A restart is needed for config changes to take effect.](#00fb9a italic)' update_available: '[HuskSync](#ff7e5e bold) [| 發現可用的新版本: v%1% (running: v%2%).](#ff7e5e)'
system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)' reload_complete: '[HuskSync](#00fb9a bold) [| 已重新載入配置和訊息文件](#00fb9a)\n[⚠ Ensure config files are up-to-date on all servers!](#00fb9a)\n[A restart is needed for config changes to take effect.](#00fb9a italic)'
error_invalid_syntax: '[錯誤:](#ff3300) [語法不正確,用法:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)' system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)'
error_invalid_player: '[錯誤:](#ff3300) [找不到這位玩家](#ff7e5e)' error_invalid_syntax: '[錯誤:](#ff3300) [語法不正確,用法:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)'
error_no_permission: '[錯誤:](#ff3300) [您沒有權限執行這個指令](#ff7e5e)' error_invalid_player: '[錯誤:](#ff3300) [找不到這位玩家](#ff7e5e)'
error_console_command_only: '[錯誤:](#ff3300) [該指令只能透過 控制台 執行](#ff7e5e)' error_no_permission: '[錯誤:](#ff3300) [您沒有權限執行這個指令](#ff7e5e)'
error_in_game_command_only: '[錯誤:](#ff3300) [該指令只能在遊戲內執行](#ff7e5e)' error_console_command_only: '[錯誤:](#ff3300) [該指令只能透過 控制台 執行](#ff7e5e)'
error_no_data_to_display: '[錯誤:](#ff3300) [找不到任何可顯示的用戶資訊.](#ff7e5e)' error_in_game_command_only: '[錯誤:](#ff3300) [該指令只能在遊戲內執行](#ff7e5e)'
error_invalid_version_uuid: '[錯誤:](#ff3300) [找不到正確的 Version UUID.](#ff7e5e)' error_no_data_to_display: '[錯誤:](#ff3300) [找不到任何可顯示的用戶資訊.](#ff7e5e)'
husksync_command_description: 'Manage the HuskSync plugin' error_invalid_version_uuid: '[錯誤:](#ff3300) [找不到正確的 Version UUID.](#ff7e5e)'
userdata_command_description: 'View, manage & restore player userdata' husksync_command_description: 'Manage the HuskSync plugin'
inventory_command_description: 'View & edit a player''s inventory' userdata_command_description: 'View, manage & restore player userdata'
enderchest_command_description: 'View & edit a player''s Ender Chest' inventory_command_description: 'View & edit a player''s inventory'
enderchest_command_description: 'View & edit a player''s Ender Chest'

View File

@@ -19,9 +19,8 @@
package net.william278.husksync.config; package net.william278.husksync.config;
import net.william278.annotaml.Annotaml; import de.exlll.configlib.YamlConfigurations;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@@ -30,14 +29,14 @@ import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.MethodSource;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.URL; import java.net.URL;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.stream.Stream; import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.*;
@DisplayName("Locales Tests") @DisplayName("Locales Tests")
public class LocalesTests { public class LocalesTests {
@@ -48,10 +47,10 @@ public class LocalesTests {
@Test @Test
public void testLoadEnglishLocales() { public void testLoadEnglishLocales() {
try (InputStream locales = LocalesTests.class.getClassLoader().getResourceAsStream("locales/en-gb.yml")) { try (InputStream locales = LocalesTests.class.getClassLoader().getResourceAsStream("locales/en-gb.yml")) {
Assertions.assertNotNull(locales, "en-gb.yml is missing from the locales folder"); assertNotNull(locales, "en-gb.yml is missing from the locales folder");
englishLocales = Annotaml.create(Locales.class, locales).get(); englishLocales = YamlConfigurations.read(locales, Locales.class);
} catch (IOException | InvocationTargetException | InstantiationException | IllegalAccessException e) { } catch (Throwable e) {
throw new RuntimeException(e); fail("Failed to load en-gb.yml", e);
} }
} }
@@ -59,23 +58,20 @@ public class LocalesTests {
@DisplayName("Test All Locale Keys Present") @DisplayName("Test All Locale Keys Present")
@MethodSource("provideLocaleFiles") @MethodSource("provideLocaleFiles")
public void testAllLocaleKeysPresent(@NotNull File file, @SuppressWarnings("unused") @NotNull String keyName) { public void testAllLocaleKeysPresent(@NotNull File file, @SuppressWarnings("unused") @NotNull String keyName) {
try { final Set<String> fileKeys = YamlConfigurations.load(file.toPath(), Locales.class).locales.keySet();
final Set<String> fileKeys = Annotaml.create(file, Locales.class).get().rawLocales.keySet(); englishLocales.locales.keySet().forEach(key -> assertTrue(
englishLocales.rawLocales.keySet() fileKeys.contains(key), "Locale key " + key + " is missing from " + file.getName()
.forEach(key -> Assertions.assertTrue(fileKeys.contains(key), ));
"Locale key " + key + " is missing from " + file.getName()));
} catch (IOException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
} }
@NotNull @NotNull
private static Stream<Arguments> provideLocaleFiles() { private static Stream<Arguments> provideLocaleFiles() {
URL url = LocalesTests.class.getClassLoader().getResource("locales"); final URL url = LocalesTests.class.getClassLoader().getResource("locales");
Assertions.assertNotNull(url, "locales folder is missing"); assertNotNull(url, "locales folder is missing");
return Stream.of(Objects.requireNonNull(new File(url.getPath())
.listFiles(file -> file.getName().endsWith("yml") && !file.getName().equals("en-gb.yml")))) return Stream.of(Objects.requireNonNull(new File(url.getPath()).listFiles(
.map(file -> Arguments.of(file, file.getName().replace(".yml", ""))); file -> file.getName().endsWith("yml") && !file.getName().equals("en-gb.yml")
))).map(file -> Arguments.of(file, file.getName().replace(".yml", "")));
} }
} }

View File

@@ -24,11 +24,11 @@ import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@DisplayName("Plan Hook Tests") @DisplayName("Plan Hook Tests")
public class PlanDataExtensionTests { public class PlanHookTests {
@Test @Test
@DisplayName("Test Plan Hook Implementation") @DisplayName("Test Plan Data Extension")
public void testPlanHookImplementation() { public void testPlanDataExtension() {
new ExtensionExtractor(new PlanHook.PlanDataExtension()).validateAnnotations(); new ExtensionExtractor(new PlanHook.PlanDataExtension()).validateAnnotations();
} }

View File

@@ -1,8 +1,10 @@
HuskSync provides three API events your plugin can listen to when certain parts of the data synchronization process are performed. These events deal with HuskSync class types, so you may want to familiarize yourself with the [API basics](API) first. Two of the events can be cancelled (thus aborting the synchronization process at certain stages), and some events expose methods letting you affect their outcome (such as modifying the data that is saved during the process). HuskSync provides three API events your plugin can listen to when certain parts of the data synchronization process are performed. These events deal with HuskSync class types, so you may want to familiarize yourself with the [API basics](API) first. Two of the events can be cancelled (thus aborting the synchronization process at certain stages), and some events expose methods letting you affect their outcome (such as modifying the data that is saved during the process).
Consult the Javadocs for more information&mdash;and don't forget to register your listener when listening for these event calls. Please note that carrying out expensive blocking operations during these events is strongly discouraged as this may affect plugin performance. Consult the Javadocs for more information. Please note that carrying out expensive blocking operations during these events is strongly discouraged as this may affect plugin performance.
## List of API Events ## Bukkit Platform Events
> **Tip:** Don't forget to register your listener when listening for these event calls.
>
| Bukkit Event class | Cancellable | Description | | Bukkit Event class | Cancellable | Description |
|---------------------------|:-----------:|---------------------------------------------------------------------------------------------| |---------------------------|:-----------:|---------------------------------------------------------------------------------------------|
| `BukkitDataSaveEvent` | ✅ | Called when player data snapshot is created, saved and cached due to a DataSaveCause | | `BukkitDataSaveEvent` | ✅ | Called when player data snapshot is created, saved and cached due to a DataSaveCause |

View File

@@ -1,7 +1,7 @@
The HuskSync API (v3) provides methods for retrieving and updating [data snapshots](Data-Snapshot-API), a number of [[API Events]] for tracking when user data is synced and saved, and infrastructure for registering serializers to [synchronise custom data types](Custom-Data-API). The HuskSync API (v3) provides methods for retrieving and updating [data snapshots](Data-Snapshot-API), a number of [[API Events]] for tracking when user data is synced and saved, and infrastructure for registering serializers to [synchronise custom data types](Custom-Data-API).
## Compatibility ## Compatibility
[![Maven](https://repo.william278.net/api/badge/latest/releases/net/william278/husksync?color=00fb9a&name=Maven&prefix=v)](https://repo.william278.net/#/releases/net/william278/husksync/) [![Maven](https://repo.william278.net/api/badge/latest/releases/net/william278/husksync/husksync-common?color=00fb9a&name=Maven&prefix=v)](https://repo.william278.net/#/releases/net/william278/husksync/)
The HuskSync API shares version numbering with the plugin itself for consistency and convenience. Please note minor and patch plugin releases may make API additions and deprecations, but will not introduce breaking changes without notice. The HuskSync API shares version numbering with the plugin itself for consistency and convenience. Please note minor and patch plugin releases may make API additions and deprecations, but will not introduce breaking changes without notice.
@@ -11,10 +11,20 @@ The HuskSync API shares version numbering with the plugin itself for consistency
| v2.x | _v2.0&mdash;v2.2.8_ | ❌ | | v2.x | _v2.0&mdash;v2.2.8_ | ❌ |
| v1.x | _v1.0&mdash;v1.4.1_ | ❌️ | | v1.x | _v1.0&mdash;v1.4.1_ | ❌️ |
### Platforms
> **Note:** For versions older than `v3.3`, the HuskSync API was only distributed for the Bukkit platform (as `net.william278:husksync`)
The HuskSync API is available for the following platforms:
* `bukkit` - Bukkit, Spigot, Paper, etc. Provides Bukkit API event listeners and adapters to `org.bukkit` objects.
* `common` - Common API for all platforms.
<details> <details>
<summary>Targeting older versions</summary> <summary>Targeting older versions</summary>
HuskSync versions prior to `v2.2.5` are distributed on [JitPack](https://jitpack.io/#/net/william278/HuskSync), and you will need to use the `https://jitpack.io` repository instead. * The HuskSync API was only distributed for the Bukkit module prior to `v3.3`; the artifact ID was `net.william278:husksync` instead of `net.william278.husksync:husksync-PLATFORM`.
* HuskSync versions prior to `v2.2.5` are distributed on [JitPack](https://jitpack.io/#/net/william278/HuskSync), and you will need to use the `https://jitpack.io` repository instead.
</details> </details>
## Table of Contents ## Table of Contents
@@ -44,8 +54,8 @@ Add the repository to your `pom.xml` as per below. You can alternatively specify
Add the dependency to your `pom.xml` as per below. Replace `VERSION` with the latest version of HuskSync (without the v): ![Latest version](https://img.shields.io/github/v/tag/WiIIiam278/HuskSync?color=%23282828&label=%20&style=flat-square) Add the dependency to your `pom.xml` as per below. Replace `VERSION` with the latest version of HuskSync (without the v): ![Latest version](https://img.shields.io/github/v/tag/WiIIiam278/HuskSync?color=%23282828&label=%20&style=flat-square)
```xml ```xml
<dependency> <dependency>
<groupId>net.william278</groupId> <groupId>net.william278.husksync</groupId>
<artifactId>husksync</artifactId> <artifactId>husksync-PLATFORM</artifactId>
<version>VERSION</version> <version>VERSION</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
@@ -68,7 +78,7 @@ Add the dependency as per below. Replace `VERSION` with the latest version of Hu
```groovy ```groovy
dependencies { dependencies {
compileOnly 'net.william278:husksync:VERSION' compileOnly 'net.william278.husksync:husksync-PLATFORM:VERSION'
} }
``` ```
</details> </details>
@@ -117,9 +127,10 @@ public class MyPlugin extends JavaPlugin {
## 5. Getting an instance of the API ## 5. Getting an instance of the API
- You can now get the API instance by calling `HuskSyncAPI#getInstance()` - You can now get the API instance by calling `HuskSyncAPI#getInstance()`
- If targeting the Bukkit platform, you can also use `BukkitHuskSyncAPI#getBukkitInstance()` to get the Bukkit-extended API instance (recommended)
```java ```java
import net.william278.husksync.api.BukkitHuskSyncAPI; import net.william278.husksync.api.HuskSyncAPI;
public class HuskSyncAPIHook { public class HuskSyncAPIHook {

View File

@@ -18,7 +18,9 @@ This page contains the configuration structure for HuskSync.
# ┣╸ Information: https://william278.net/project/husksync # ┣╸ Information: https://william278.net/project/husksync
# ┣╸ Config Help: https://william278.net/docs/husksync/config-file/ # ┣╸ Config Help: https://william278.net/docs/husksync/config-file/
# ┗╸ Documentation: https://william278.net/docs/husksync # ┗╸ Documentation: https://william278.net/docs/husksync
# Locale of the default language file to use. Docs: https://william278.net/docs/husksync/translations
# Locale of the default language file to use.
# Docs: https://william278.net/docs/husksync/translations
language: en-gb language: en-gb
# Whether to automatically check for plugin updates on startup # Whether to automatically check for plugin updates on startup
check_for_updates: true check_for_updates: true
@@ -28,21 +30,23 @@ cluster_id: ''
debug_logging: false debug_logging: false
# Whether to provide modern, rich TAB suggestions for commands (if available) # Whether to provide modern, rich TAB suggestions for commands (if available)
brigadier_tab_completion: false brigadier_tab_completion: false
# Whether to enable the Player Analytics hook. Docs: https://william278.net/docs/husksync/plan-hook # Whether to enable the Player Analytics hook.
# Docs: https://william278.net/docs/husksync/plan-hook
enable_plan_hook: true enable_plan_hook: true
# Database settings
database: database:
# Type of database to use (MYSQL, MARIADB) # Type of database to use (MYSQL, MARIADB)
type: MYSQL type: MYSQL
credentials:
# Specify credentials here for your MYSQL or MARIADB database # Specify credentials here for your MYSQL or MARIADB database
credentials:
host: localhost host: localhost
port: 3306 port: 3306
database: HuskSync database: HuskSync
username: root username: root
password: pa55w0rd password: pa55w0rd
parameters: ?autoReconnect=true&useSSL=false&useUnicode=true&characterEncoding=UTF-8 parameters: ?autoReconnect=true&useSSL=false&useUnicode=true&characterEncoding=UTF-8
connection_pool:
# MYSQL / MARIADB database Hikari connection pool properties. Don't modify this unless you know what you're doing! # MYSQL / MARIADB database Hikari connection pool properties. Don't modify this unless you know what you're doing!
connection_pool:
maximum_pool_size: 10 maximum_pool_size: 10
minimum_idle: 10 minimum_idle: 10
maximum_lifetime: 1800000 maximum_lifetime: 1800000
@@ -52,21 +56,32 @@ database:
table_names: table_names:
users: husksync_users users: husksync_users
user_data: husksync_user_data user_data: husksync_user_data
# Redis settings
redis: redis:
credentials:
# Specify the credentials of your Redis database here. Set "password" to '' if you don't have one # Specify the credentials of your Redis database here. Set "password" to '' if you don't have one
credentials:
host: localhost host: localhost
port: 6379 port: 6379
password: '' password: ''
use_ssl: false use_ssl: false
# Options for if you're using Redis sentinel. Don't modify this unless you know what you're doing!
sentinel:
# The master set name for the Redis sentinel.
master: ''
# List of host:port pairs
nodes: []
password: ''
# Redis settings
synchronization: synchronization:
# The data synchronization mode to use (LOCKSTEP or DELAY). LOCKSTEP is recommended for most networks. Docs: https://william278.net/docs/husksync/sync-modes # The data synchronization mode to use (LOCKSTEP or DELAY). LOCKSTEP is recommended for most networks.
# Docs: https://william278.net/docs/husksync/sync-modes
mode: LOCKSTEP mode: LOCKSTEP
# The number of data snapshot backups that should be kept at once per user # The number of data snapshot backups that should be kept at once per user
max_user_data_snapshots: 16 max_user_data_snapshots: 16
# Number of hours between new snapshots being saved as backups (Use "0" to backup all snapshots) # Number of hours between new snapshots being saved as backups (Use "0" to backup all snapshots)
snapshot_backup_frequency: 4 snapshot_backup_frequency: 4
# List of save cause IDs for which a snapshot will be automatically pinned (so it won't be rotated). Docs: https://william278.net/docs/husksync/data-rotation#save-causes # List of save cause IDs for which a snapshot will be automatically pinned (so it won't be rotated).
# Docs: https://william278.net/docs/husksync/data-rotation#save-causes
auto_pinned_save_causes: auto_pinned_save_causes:
- INVENTORY_COMMAND - INVENTORY_COMMAND
- ENDERCHEST_COMMAND - ENDERCHEST_COMMAND
@@ -75,29 +90,30 @@ synchronization:
- MPDB_MIGRATION - MPDB_MIGRATION
# Whether to create a snapshot for users on a world when the server saves that world # Whether to create a snapshot for users on a world when the server saves that world
save_on_world_save: true save_on_world_save: true
# Configuration for how and when to sync player data when they die
save_on_death: save_on_death:
# Whether to create a snapshot for users when they die (containing their death drops) # Whether to create a snapshot for users when they die (containing their death drops)
enabled: true enabled: false
# What items to save in death snapshots? (DROPS or ITEMS_TO_KEEP). Note that ITEMS_TO_KEEP (suggested for keepInventory servers) requires a Paper 1.19.4+ server # What items to save in death snapshots? (DROPS or ITEMS_TO_KEEP). Note that ITEMS_TO_KEEP (suggested for keepInventory servers) requires a Paper 1.19.4+ server.
items_to_save: DROPS items_to_save: DROPS
# Should a death snapshot still be created even if the items to save on the player's death are empty? # Should a death snapshot still be created even if the items to save on the player's death are empty?
save_empty_items: false save_empty_items: true
# Whether dead players who log out and log in to a different server should have their items saved. # Whether dead players who log out and log in to a different server should have their items saved.
sync_dead_players_changing_server: true sync_dead_players_changing_server: true
# Whether to use the snappy data compression algorithm. Keep on unless you know what you're doing # Whether to use the snappy data compression algorithm. Keep on unless you know what you're doing
compress_data: true compress_data: true
# Where to display sync notifications (ACTION_BAR, CHAT, TOAST or NONE) # Where to display sync notifications (ACTION_BAR, CHAT, TOAST or NONE)
notification_display_slot: ACTION_BAR notification_display_slot: ACTION_BAR
# (Experimental) Persist Cartography Table locked maps to let them be viewed on any server # Persist maps locked in a Cartography Table to let them be viewed on any server
persist_locked_maps: true persist_locked_maps: true
# Whether to synchronize player max health (requires health syncing to be enabled) # Whether to synchronize player max health (requires health syncing to be enabled)
synchronize_max_health: true synchronize_max_health: true
# If using the DELAY sync method, how long should this server listen for Redis key data updates before pulling data from the database instead (i.e., if the user did not change servers). # If using the DELAY sync method, how long should this server listen for Redis key data updates before pulling data from the database instead (i.e., if the user did not change servers).
network_latency_milliseconds: 500 network_latency_milliseconds: 500
# Which data types to synchronize (Docs: https://william278.net/docs/husksync/sync-features) # Which data types to synchronize.
# Docs: https://william278.net/docs/husksync/sync-features
features: features:
hunger: true persistent_data: true
persistent_data: false
inventory: true inventory: true
game_mode: true game_mode: true
advancements: true advancements: true
@@ -107,6 +123,7 @@ synchronization:
location: false location: false
statistics: true statistics: true
health: true health: true
hunger: true
# Commands which should be blocked before a player has finished syncing (Use * to block all commands) # Commands which should be blocked before a player has finished syncing (Use * to block all commands)
blacklisted_commands_while_locked: blacklisted_commands_while_locked:
- '*' - '*'
@@ -124,11 +141,12 @@ synchronization:
```yaml ```yaml
# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ # ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
# ┃ HuskSync Server ID config # ┃ HuskSync - Server ID
# ┃ Developed by William278 ┃ # ┃ Developed by William278 ┃
# ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ # ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
# ┣╸ This file should contain the ID of this server as defined in your proxy config. # ┣╸ This file should contain the ID of this server as defined in your proxy config.
# ┗╸ If you join it using /server alpha, then set it to 'alpha' (case-sensitive) # ┗╸ If you join it using /server alpha, then set it to 'alpha' (case-sensitive)
name: beta name: beta
``` ```

View File

@@ -1,4 +1,4 @@
HuskSync allows you to save and synchronize custom data through the existing versatile DataSnapshot format. This page assumes you've read the [[API]] introduction and are familiar with the aforementioned [[Data Snapshot API]]. HuskSync allows you to save and synchronize custom data through the existing versatile DataSnapshot format. This page assumes you've read the [[API]] introduction and are familiar with the aforementioned [[Data Snapshot API]]. This page discusses API implementations that target the Bukkit platform.
To do this, you create and register an implementation of a platform `Data` class (e.g., `BukkitData`) and a corresponding `Serializer` class (e.g., `BukkitSerializer`). You can then apply your custom data type to a user using the `OnlineUser#setData(Identifier, Data)` method. To do this, you create and register an implementation of a platform `Data` class (e.g., `BukkitData`) and a corresponding `Serializer` class (e.g., `BukkitSerializer`). You can then apply your custom data type to a user using the `OnlineUser#setData(Identifier, Data)` method.

View File

@@ -5,7 +5,7 @@ This will walk you through installing HuskSync on your network of Spigot servers
* A MySQL Database (v8.0+) * A MySQL Database (v8.0+)
* A Redis Database (v5.0+) &mdash; see [[FAQs]] for more details. * A Redis Database (v5.0+) &mdash; see [[FAQs]] for more details.
* Any number of Spigot servers, connected by a BungeeCord or Velocity-based proxy (Minecraft v1.16.5+, running Java 16+) * Any number of Spigot servers, connected by a BungeeCord or Velocity-based proxy (Minecraft v1.17.1+, running Java 17+)
## Setup Instructions ## Setup Instructions
### 1. Install the jar ### 1. Install the jar

View File

@@ -5,6 +5,7 @@ This plugin does not support the following software-Minecraft version combinatio
|--------------------|-------------------------------------------|----------------------------------------| |--------------------|-------------------------------------------|----------------------------------------|
| 1.19.4 | Only: `Purpur, Pufferfish`&dagger; | Older Paper builds also not supported. | | 1.19.4 | Only: `Purpur, Pufferfish`&dagger; | Older Paper builds also not supported. |
| 1.19.3 | Only: `Paper, Purpur, Pufferfish`&dagger; | Upgrade to 1.19.4 or use Spigot | | 1.19.3 | Only: `Paper, Purpur, Pufferfish`&dagger; | Upgrade to 1.19.4 or use Spigot |
| below 1.16.5 | _All_ | Upgrade to 1.16.5 | | 1.16.5 | _All_ | Please use v3.3.1 or lower |
| below 1.16.5 | _All_ | Upgrade Minecraft 1.16.5 |
&dagger;Further downstream forks of this server software are also affected. &dagger;Further downstream forks of this server software are also affected.

View File

@@ -1,13 +1,13 @@
org.gradle.jvmargs='-Dfile.encoding=UTF-8' org.gradle.jvmargs='-Dfile.encoding=UTF-8'
org.gradle.daemon=true org.gradle.daemon=true
javaVersion=16 javaVersion=17
plugin_version=3.2.1 plugin_version=3.3
plugin_archive=husksync plugin_archive=husksync
plugin_description=A modern, cross-server player data synchronization system plugin_description=A modern, cross-server player data synchronization system
jedis_version=5.1.0 jedis_version=5.1.0
mysql_driver_version=8.2.0 mysql_driver_version=8.3.0
mariadb_driver_version=3.3.2 mariadb_driver_version=3.3.2
snappy_version=1.1.10.5 snappy_version=1.1.10.5

View File

@@ -1,12 +0,0 @@
# This file ensures jitpack builds HuskSync correctly by setting the JDK to 16
jdk:
- 'openjdk16'
before_install:
- 'git clone https://github.com/WiIIiam278/HuskSync.git --recurse-submodules'
- 'chmod +x gradlew'
- 'chmod +x ./.scripts/ensure-java-16'
- 'bash ./.scripts/ensure-java-16 install'
install:
- 'if ! ./.scripts/ensure-java-16 use; then source ~/.sdkman/bin/sdkman-init.sh; fi'
- 'java -version'
- './gradlew publishToMavenLocal'

View File

@@ -3,6 +3,10 @@ dependencies {
compileOnly project(':common') compileOnly project(':common')
compileOnly 'io.papermc.paper:paper-api:1.19.4-R0.1-SNAPSHOT' compileOnly 'io.papermc.paper:paper-api:1.19.4-R0.1-SNAPSHOT'
compileOnly 'org.jetbrains:annotations:24.1.0'
compileOnly 'org.projectlombok:lombok:1.18.30'
annotationProcessor 'org.projectlombok:lombok:1.18.30'
} }
shadowJar { shadowJar {
@@ -14,27 +18,26 @@ shadowJar {
relocate 'org.apache.commons.text', 'net.william278.husksync.libraries.commons.text' relocate 'org.apache.commons.text', 'net.william278.husksync.libraries.commons.text'
relocate 'org.apache.commons.lang3', 'net.william278.husksync.libraries.commons.lang3' relocate 'org.apache.commons.lang3', 'net.william278.husksync.libraries.commons.lang3'
relocate 'com.google.gson', 'net.william278.husksync.libraries.gson' relocate 'com.google.gson', 'net.william278.husksync.libraries.gson'
relocate 'org.json', 'net.william278.husksync.libraries.json'
relocate 'com.fatboyindustrial', 'net.william278.husksync.libraries' relocate 'com.fatboyindustrial', 'net.william278.husksync.libraries'
relocate 'de.themoep', 'net.william278.husksync.libraries' relocate 'de.themoep', 'net.william278.husksync.libraries'
relocate 'org.jetbrains', 'net.william278.husksync.libraries' relocate 'org.jetbrains', 'net.william278.husksync.libraries'
relocate 'org.intellij', 'net.william278.husksync.libraries' relocate 'org.intellij', 'net.william278.husksync.libraries'
relocate 'com.zaxxer', 'net.william278.husksync.libraries' relocate 'com.zaxxer', 'net.william278.husksync.libraries'
relocate 'dev.dejvokep', 'net.william278.husksync.libraries' relocate 'de.exlll', 'net.william278.huskclaims.libraries'
relocate 'net.william278.desertwell', 'net.william278.husksync.libraries.desertwell' relocate 'net.william278.desertwell', 'net.william278.husksync.libraries.desertwell'
relocate 'net.william278.paginedown', 'net.william278.husksync.libraries.paginedown' relocate 'net.william278.paginedown', 'net.william278.husksync.libraries.paginedown'
relocate 'net.william278.mapdataapi', 'net.william278.husksync.libraries.mapdataapi' relocate 'net.william278.mapdataapi', 'net.william278.husksync.libraries.mapdataapi'
relocate 'net.william278.andjam', 'net.william278.husksync.libraries.andjam' relocate 'net.william278.andjam', 'net.william278.husksync.libraries.andjam'
relocate 'net.querz', 'net.william278.husksync.libraries.nbtparser'
relocate 'net.roxeez', 'net.william278.husksync.libraries'
relocate 'me.lucko.commodore', 'net.william278.husksync.libraries.commodore'
relocate 'net.byteflux.libby', 'net.william278.husksync.libraries.libby'
relocate 'org.bstats', 'net.william278.husksync.libraries.bstats'
relocate 'dev.triumphteam.gui', 'net.william278.husksync.libraries.triumphgui'
relocate 'net.william278.mpdbconverter', 'net.william278.husksync.libraries.mpdbconverter' relocate 'net.william278.mpdbconverter', 'net.william278.husksync.libraries.mpdbconverter'
relocate 'net.william278.hslmigrator', 'net.william278.husksync.libraries.hslconverter' relocate 'net.william278.hslmigrator', 'net.william278.husksync.libraries.hslconverter'
relocate 'net.william278.annotaml', 'net.william278.husksync.libraries.annotaml' relocate 'org.json', 'net.william278.husksync.libraries.json'
relocate 'net.querz', 'net.william278.husksync.libraries.nbtparser'
relocate 'net.roxeez', 'net.william278.husksync.libraries'
relocate 'me.lucko.commodore', 'net.william278.husksync.libraries.commodore'
relocate 'org.bstats', 'net.william278.husksync.libraries.bstats'
relocate 'dev.triumphteam.gui', 'net.william278.husksync.libraries.triumphgui'
relocate 'space.arim.morepaperlib', 'net.william278.husksync.libraries.paperlib' relocate 'space.arim.morepaperlib', 'net.william278.husksync.libraries.paperlib'
relocate 'de.tr7zw.changeme.nbtapi', 'net.william278.husksync.libraries.nbtapi' relocate 'de.tr7zw.changeme.nbtapi', 'net.william278.husksync.libraries.nbtapi'
minimize()
} }

View File

@@ -19,12 +19,12 @@
package net.william278.husksync; package net.william278.husksync;
import de.exlll.configlib.Configuration;
import de.exlll.configlib.YamlConfigurations;
import io.papermc.paper.plugin.loader.PluginClasspathBuilder; import io.papermc.paper.plugin.loader.PluginClasspathBuilder;
import io.papermc.paper.plugin.loader.PluginLoader; import io.papermc.paper.plugin.loader.PluginLoader;
import io.papermc.paper.plugin.loader.library.impl.MavenLibraryResolver; import io.papermc.paper.plugin.loader.library.impl.MavenLibraryResolver;
import net.william278.annotaml.Annotaml; import lombok.NoArgsConstructor;
import net.william278.annotaml.YamlFile;
import net.william278.annotaml.YamlKey;
import org.eclipse.aether.artifact.DefaultArtifact; import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.graph.Dependency; import org.eclipse.aether.graph.Dependency;
import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.repository.RemoteRepository;
@@ -35,12 +35,13 @@ import java.io.InputStream;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@SuppressWarnings({"UnstableApiUsage", "unused"}) @NoArgsConstructor
@SuppressWarnings("UnstableApiUsage")
public class PaperHuskSyncLoader implements PluginLoader { public class PaperHuskSyncLoader implements PluginLoader {
@Override @Override
public void classloader(@NotNull PluginClasspathBuilder classpathBuilder) { public void classloader(@NotNull PluginClasspathBuilder classpathBuilder) {
MavenLibraryResolver resolver = new MavenLibraryResolver(); final MavenLibraryResolver resolver = new MavenLibraryResolver();
resolveLibraries(classpathBuilder).stream() resolveLibraries(classpathBuilder).stream()
.map(DefaultArtifact::new) .map(DefaultArtifact::new)
@@ -55,8 +56,11 @@ public class PaperHuskSyncLoader implements PluginLoader {
@NotNull @NotNull
private static List<String> resolveLibraries(@NotNull PluginClasspathBuilder classpathBuilder) { private static List<String> resolveLibraries(@NotNull PluginClasspathBuilder classpathBuilder) {
try (InputStream input = getLibraryListFile()) { try (InputStream input = getLibraryListFile()) {
return Annotaml.create(PaperLibraries.class, Objects.requireNonNull(input)).get().libraries; return YamlConfigurations.read(
} catch (Exception e) { Objects.requireNonNull(input, "Failed to read libraries file"),
PaperLibraries.class
).libraries;
} catch (Throwable e) {
classpathBuilder.getContext().getLogger().error("Failed to resolve libraries", e); classpathBuilder.getContext().getLogger().error("Failed to resolve libraries", e);
} }
return List.of(); return List.of();
@@ -67,16 +71,12 @@ public class PaperHuskSyncLoader implements PluginLoader {
return PaperHuskSyncLoader.class.getClassLoader().getResourceAsStream("paper-libraries.yml"); return PaperHuskSyncLoader.class.getClassLoader().getResourceAsStream("paper-libraries.yml");
} }
@YamlFile(header = "Dependencies for HuskSync on Paper") @Configuration
@NoArgsConstructor
public static class PaperLibraries { public static class PaperLibraries {
@YamlKey("libraries")
private List<String> libraries; private List<String> libraries;
@SuppressWarnings("unused")
private PaperLibraries() {
}
} }
} }

View File

@@ -29,6 +29,8 @@ import org.jetbrains.annotations.NotNull;
import java.util.List; import java.util.List;
import static net.william278.husksync.config.Settings.SynchronizationSettings.SaveOnDeathSettings;
public class PaperEventListener extends BukkitEventListener { public class PaperEventListener extends BukkitEventListener {
public PaperEventListener(@NotNull BukkitHuskSync plugin) { public PaperEventListener(@NotNull BukkitHuskSync plugin) {
@@ -46,13 +48,14 @@ public class PaperEventListener extends BukkitEventListener {
} }
// Handle saving player data snapshots on death // Handle saving player data snapshots on death
if (!plugin.getSettings().doSaveOnDeath()) { final SaveOnDeathSettings settings = plugin.getSettings().getSynchronization().getSaveOnDeath();
if (!settings.isEnabled()) {
return; return;
} }
// Paper - support saving the player's items to keep if enabled // Paper - support saving the player's items to keep if enabled
final int maxInventorySize = BukkitData.Items.Inventory.INVENTORY_SLOT_COUNT; final int maxInventorySize = BukkitData.Items.Inventory.INVENTORY_SLOT_COUNT;
final List<ItemStack> itemsToSave = switch (plugin.getSettings().getDeathItemsMode()) { final List<ItemStack> itemsToSave = switch (settings.getItemsToSave()) {
case DROPS -> event.getDrops(); case DROPS -> event.getDrops();
case ITEMS_TO_KEEP -> event.getItemsToKeep(); case ITEMS_TO_KEEP -> event.getItemsToKeep();
}; };

View File

@@ -1,4 +0,0 @@
dependencies {
implementation project(path: ':bukkit', configuration: 'shadow')
runtimeOnly project(path: ':paper')
}

View File

@@ -8,6 +8,5 @@ rootProject.name = 'HuskSync'
include( include(
'common', 'common',
'bukkit', 'bukkit',
'paper', 'paper'
'plugin'
) )

View File

@@ -20,7 +20,7 @@ class Parameters:
backend_ports = [25567, 25568] backend_ports = [25567, 25568]
backend_type = 'paper' backend_type = 'paper'
backend_ram = 2048 backend_ram = 2048
backend_plugins = ['../target/HuskSync-Plugin-*.jar'] backend_plugins = ['../target/HuskSync-Paper-*.jar']
backend_plugin_folders = ['./HuskSync'] backend_plugin_folders = ['./HuskSync']
operator_names = ['William278'] operator_names = ['William278']
operator_uuids = ['5dfb0558-e306-44f4-bb9a-f9218d4eb787'] operator_uuids = ['5dfb0558-e306-44f4-bb9a-f9218d4eb787']