mirror of
https://github.com/WiIIiam278/HuskSync.git
synced 2025-12-25 17:49:20 +00:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
414246f243 | ||
|
|
a3e269c00b | ||
|
|
bf9f29ffe9 | ||
|
|
29bd2e1319 | ||
|
|
2475a9b3c6 | ||
|
|
2a52cc9086 | ||
|
|
237abf9698 | ||
|
|
adbc264532 | ||
|
|
f9cfec7d03 | ||
|
|
29805bfe04 | ||
|
|
8d2e5a6a52 | ||
|
|
d4f61bd646 | ||
|
|
55173be04b | ||
|
|
e7078c9542 |
9
.github/workflows/update_docs.yml
vendored
9
.github/workflows/update_docs.yml
vendored
@@ -19,9 +19,6 @@ jobs:
|
|||||||
- name: 'Checkout for CI 🛎️'
|
- name: 'Checkout for CI 🛎️'
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: 'Push Docs to Github Wiki 📄️'
|
- name: 'Push Docs to Github Wiki 📄️'
|
||||||
uses: Andrew-Chen-Wang/github-wiki-action@v3
|
uses: Andrew-Chen-Wang/github-wiki-action@v4
|
||||||
env:
|
with:
|
||||||
WIKI_DIR: 'docs/'
|
path: 'docs'
|
||||||
GH_TOKEN: ${{ github.token }}
|
|
||||||
GH_MAIL: 'actions@github.com'
|
|
||||||
GH_NAME: 'github-actions[bot]'
|
|
||||||
@@ -7,7 +7,7 @@ dependencies {
|
|||||||
implementation 'net.william278:mapdataapi:1.0.3'
|
implementation 'net.william278:mapdataapi:1.0.3'
|
||||||
implementation 'net.william278:andjam:1.0.2'
|
implementation 'net.william278:andjam:1.0.2'
|
||||||
implementation 'me.lucko:commodore:2.2'
|
implementation 'me.lucko:commodore:2.2'
|
||||||
implementation 'net.kyori:adventure-platform-bukkit:4.3.1'
|
implementation 'net.kyori:adventure-platform-bukkit:4.3.2'
|
||||||
implementation 'dev.triumphteam:triumph-gui:3.1.7'
|
implementation 'dev.triumphteam:triumph-gui:3.1.7'
|
||||||
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'
|
||||||
|
|||||||
@@ -48,6 +48,8 @@ import java.util.logging.Level;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static net.william278.husksync.util.BukkitKeyedAdapter.*;
|
||||||
|
|
||||||
public abstract class BukkitData implements Data {
|
public abstract class BukkitData implements Data {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -608,7 +610,7 @@ public abstract class BukkitData implements Data {
|
|||||||
Map.Entry::getKey,
|
Map.Entry::getKey,
|
||||||
entry -> entry.getValue().entrySet().stream()
|
entry -> entry.getValue().entrySet().stream()
|
||||||
.flatMap(blockEntry -> {
|
.flatMap(blockEntry -> {
|
||||||
Material material = Material.matchMaterial(blockEntry.getKey());
|
Material material = matchMaterial(blockEntry.getKey());
|
||||||
return material != null ? Stream.of(new AbstractMap.SimpleEntry<>(material, blockEntry.getValue())) : Stream.empty();
|
return material != null ? Stream.of(new AbstractMap.SimpleEntry<>(material, blockEntry.getValue())) : Stream.empty();
|
||||||
})
|
})
|
||||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))
|
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))
|
||||||
@@ -622,7 +624,7 @@ public abstract class BukkitData implements Data {
|
|||||||
Map.Entry::getKey,
|
Map.Entry::getKey,
|
||||||
entry -> entry.getValue().entrySet().stream()
|
entry -> entry.getValue().entrySet().stream()
|
||||||
.flatMap(itemEntry -> {
|
.flatMap(itemEntry -> {
|
||||||
Material material = Material.matchMaterial(itemEntry.getKey());
|
Material material = matchMaterial(itemEntry.getKey());
|
||||||
return material != null ? Stream.of(new AbstractMap.SimpleEntry<>(material, itemEntry.getValue())) : Stream.empty();
|
return material != null ? Stream.of(new AbstractMap.SimpleEntry<>(material, itemEntry.getValue())) : Stream.empty();
|
||||||
})
|
})
|
||||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))
|
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))
|
||||||
@@ -661,20 +663,6 @@ public abstract class BukkitData implements Data {
|
|||||||
return new StatisticsMap(genericStats, blockStats, itemStats, entityStats);
|
return new StatisticsMap(genericStats, blockStats, itemStats, entityStats);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private static Statistic matchStatistic(@NotNull String key) {
|
|
||||||
return Arrays.stream(Statistic.values())
|
|
||||||
.filter(stat -> stat.getKey().toString().equals(key))
|
|
||||||
.findFirst().orElse(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private static EntityType matchEntityType(@NotNull String key) {
|
|
||||||
return Arrays.stream(EntityType.values())
|
|
||||||
.filter(entityType -> entityType.getKey().toString().equals(key))
|
|
||||||
.findFirst().orElse(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply(@NotNull BukkitUser user, @NotNull BukkitHuskSync plugin) throws IllegalStateException {
|
public void apply(@NotNull BukkitUser user, @NotNull BukkitHuskSync plugin) throws IllegalStateException {
|
||||||
genericStatistics.forEach((stat, value) -> applyStat(user, stat, null, value));
|
genericStatistics.forEach((stat, value) -> applyStat(user, stat, null, value));
|
||||||
@@ -708,7 +696,8 @@ public abstract class BukkitData implements Data {
|
|||||||
public Map<String, Map<String, Integer>> getBlockStatistics() {
|
public Map<String, Map<String, Integer>> getBlockStatistics() {
|
||||||
return blockStatistics.entrySet().stream().filter(entry -> entry.getKey() != null).collect(
|
return blockStatistics.entrySet().stream().filter(entry -> entry.getKey() != null).collect(
|
||||||
TreeMap::new,
|
TreeMap::new,
|
||||||
(m, e) -> m.put(e.getKey().getKey().toString(), convertStatistics(e.getValue())), TreeMap::putAll
|
(m, e) -> getKeyName(e.getKey()).ifPresent(key -> m.put(key, convertStatistics(e.getValue()))),
|
||||||
|
TreeMap::putAll
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -717,7 +706,8 @@ public abstract class BukkitData implements Data {
|
|||||||
public Map<String, Map<String, Integer>> getItemStatistics() {
|
public Map<String, Map<String, Integer>> getItemStatistics() {
|
||||||
return itemStatistics.entrySet().stream().filter(entry -> entry.getKey() != null).collect(
|
return itemStatistics.entrySet().stream().filter(entry -> entry.getKey() != null).collect(
|
||||||
TreeMap::new,
|
TreeMap::new,
|
||||||
(m, e) -> m.put(e.getKey().getKey().toString(), convertStatistics(e.getValue())), TreeMap::putAll
|
(m, e) -> getKeyName(e.getKey()).ifPresent(key -> m.put(key, convertStatistics(e.getValue()))),
|
||||||
|
TreeMap::putAll
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -726,7 +716,8 @@ public abstract class BukkitData implements Data {
|
|||||||
public Map<String, Map<String, Integer>> getEntityStatistics() {
|
public Map<String, Map<String, Integer>> getEntityStatistics() {
|
||||||
return entityStatistics.entrySet().stream().filter(entry -> entry.getKey() != null).collect(
|
return entityStatistics.entrySet().stream().filter(entry -> entry.getKey() != null).collect(
|
||||||
TreeMap::new,
|
TreeMap::new,
|
||||||
(m, e) -> m.put(e.getKey().getKey().toString(), convertStatistics(e.getValue())), TreeMap::putAll
|
(m, e) -> getKeyName(e.getKey()).ifPresent(key -> m.put(key, convertStatistics(e.getValue()))),
|
||||||
|
TreeMap::putAll
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -734,13 +725,8 @@ public abstract class BukkitData implements Data {
|
|||||||
private <T extends Keyed> Map<String, Integer> convertStatistics(@NotNull Map<T, Integer> stats) {
|
private <T extends Keyed> Map<String, Integer> convertStatistics(@NotNull Map<T, Integer> stats) {
|
||||||
return stats.entrySet().stream().filter(entry -> entry.getKey() != null).collect(
|
return stats.entrySet().stream().filter(entry -> entry.getKey() != null).collect(
|
||||||
TreeMap::new,
|
TreeMap::new,
|
||||||
(m, e) -> {
|
(m, e) -> getKeyName(e.getKey()).ifPresent(key -> m.put(key, e.getValue())),
|
||||||
try {
|
TreeMap::putAll
|
||||||
m.put(e.getKey().getKey().toString(), e.getValue());
|
|
||||||
} catch (Throwable t) {
|
|
||||||
// Ignore; skip elements with invalid keys (e.g., legacy materials)
|
|
||||||
}
|
|
||||||
}, TreeMap::putAll
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ public class MpdbMigrator extends Migrator {
|
|||||||
});
|
});
|
||||||
plugin.log(Level.INFO, "Migration complete for " + dataToMigrate.size() + " users in " + ((System.currentTimeMillis() - startTime) / 1000) + " seconds!");
|
plugin.log(Level.INFO, "Migration complete for " + dataToMigrate.size() + " users in " + ((System.currentTimeMillis() - startTime) / 1000) + " seconds!");
|
||||||
return true;
|
return true;
|
||||||
} catch (Exception e) {
|
} catch (Throwable e) {
|
||||||
plugin.log(Level.SEVERE, "Error while migrating data: " + e.getMessage() + " - are your source database credentials correct?");
|
plugin.log(Level.SEVERE, "Error while migrating data: " + e.getMessage() + " - are your source database credentials correct?");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ import java.util.Arrays;
|
|||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
import static net.william278.husksync.util.BukkitKeyedAdapter.matchMaterial;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bukkit platform implementation of an {@link OnlineUser}
|
* Bukkit platform implementation of an {@link OnlineUser}
|
||||||
*/
|
*/
|
||||||
@@ -80,7 +82,7 @@ public class BukkitUser extends OnlineUser implements BukkitUserDataHolder {
|
|||||||
public void sendToast(@NotNull MineDown title, @NotNull MineDown description,
|
public void sendToast(@NotNull MineDown title, @NotNull MineDown description,
|
||||||
@NotNull String iconMaterial, @NotNull String backgroundType) {
|
@NotNull String iconMaterial, @NotNull String backgroundType) {
|
||||||
try {
|
try {
|
||||||
final Material material = Material.matchMaterial(iconMaterial);
|
final Material material = matchMaterial(iconMaterial);
|
||||||
Toast.builder((BukkitHuskSync) plugin)
|
Toast.builder((BukkitHuskSync) plugin)
|
||||||
.setTitle(title.toComponent())
|
.setTitle(title.toComponent())
|
||||||
.setDescription(description.toComponent())
|
.setDescription(description.toComponent())
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* 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.util;
|
||||||
|
|
||||||
|
import org.bukkit.Keyed;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.Statistic;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
// Utility class for adapting "Keyed" Bukkit objects
|
||||||
|
public final class BukkitKeyedAdapter {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static Statistic matchStatistic(@NotNull String key) {
|
||||||
|
try {
|
||||||
|
return Arrays.stream(Statistic.values())
|
||||||
|
.filter(stat -> stat.getKey().toString().equals(key))
|
||||||
|
.findFirst().orElse(null);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static EntityType matchEntityType(@NotNull String key) {
|
||||||
|
try {
|
||||||
|
return Arrays.stream(EntityType.values())
|
||||||
|
.filter(entityType -> entityType.getKey().toString().equals(key))
|
||||||
|
.findFirst().orElse(null);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static Material matchMaterial(@NotNull String key) {
|
||||||
|
try {
|
||||||
|
return Material.matchMaterial(key);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Optional<String> getKeyName(@NotNull Keyed keyed) {
|
||||||
|
try {
|
||||||
|
return Optional.of(keyed.getKey().toString());
|
||||||
|
} catch (Throwable e) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
package net.william278.husksync.util;
|
package net.william278.husksync.util;
|
||||||
|
|
||||||
|
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;
|
||||||
import net.william278.husksync.data.BukkitData;
|
import net.william278.husksync.data.BukkitData;
|
||||||
@@ -43,6 +44,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.util.BukkitKeyedAdapter.*;
|
||||||
|
|
||||||
public class BukkitLegacyConverter extends LegacyConverter {
|
public class BukkitLegacyConverter extends LegacyConverter {
|
||||||
|
|
||||||
public BukkitLegacyConverter(@NotNull HuskSync plugin) {
|
public BukkitLegacyConverter(@NotNull HuskSync plugin) {
|
||||||
@@ -197,36 +200,44 @@ public class BukkitLegacyConverter extends LegacyConverter {
|
|||||||
@NotNull
|
@NotNull
|
||||||
private BukkitData.Statistics readStatisticMaps(@NotNull JSONObject untyped, @NotNull JSONObject blocks,
|
private BukkitData.Statistics readStatisticMaps(@NotNull JSONObject untyped, @NotNull JSONObject blocks,
|
||||||
@NotNull JSONObject items, @NotNull JSONObject entities) {
|
@NotNull JSONObject items, @NotNull JSONObject entities) {
|
||||||
final Map<Statistic, Integer> genericStats = new HashMap<>();
|
// Read generic stats
|
||||||
untyped.keys().forEachRemaining(stat -> genericStats.put(Statistic.valueOf(stat), untyped.getInt(stat)));
|
final Map<Statistic, Integer> genericStats = Maps.newHashMap();
|
||||||
|
untyped.keys().forEachRemaining(stat -> genericStats.put(matchStatistic(stat), untyped.getInt(stat)));
|
||||||
|
|
||||||
final Map<Statistic, Map<Material, Integer>> blockStats = new HashMap<>();
|
// Read block & item stats
|
||||||
blocks.keys().forEachRemaining(stat -> {
|
final Map<Statistic, Map<Material, Integer>> blockStats, itemStats;
|
||||||
final JSONObject blockStat = blocks.getJSONObject(stat);
|
blockStats = readMaterialStatistics(blocks);
|
||||||
final Map<Material, Integer> blockMap = new HashMap<>();
|
itemStats = readMaterialStatistics(items);
|
||||||
blockStat.keys().forEachRemaining(block -> blockMap.put(Material.valueOf(block), blockStat.getInt(block)));
|
|
||||||
blockStats.put(Statistic.valueOf(stat), blockMap);
|
|
||||||
});
|
|
||||||
|
|
||||||
final Map<Statistic, Map<Material, Integer>> itemStats = new HashMap<>();
|
// Read entity stats
|
||||||
items.keys().forEachRemaining(stat -> {
|
final Map<Statistic, Map<EntityType, Integer>> entityStats = Maps.newHashMap();
|
||||||
final JSONObject itemStat = items.getJSONObject(stat);
|
|
||||||
final Map<Material, Integer> itemMap = new HashMap<>();
|
|
||||||
itemStat.keys().forEachRemaining(item -> itemMap.put(Material.valueOf(item), itemStat.getInt(item)));
|
|
||||||
itemStats.put(Statistic.valueOf(stat), itemMap);
|
|
||||||
});
|
|
||||||
|
|
||||||
final Map<Statistic, Map<EntityType, Integer>> entityStats = new HashMap<>();
|
|
||||||
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 = new HashMap<>();
|
||||||
entityStat.keys().forEachRemaining(entity -> entityMap.put(EntityType.valueOf(entity), entityStat.getInt(entity)));
|
entityStat.keys().forEachRemaining(entity -> entityMap.put(matchEntityType(entity), entityStat.getInt(entity)));
|
||||||
entityStats.put(Statistic.valueOf(stat), entityMap);
|
entityStats.put(matchStatistic(stat), entityMap);
|
||||||
});
|
});
|
||||||
|
|
||||||
return BukkitData.Statistics.from(genericStats, blockStats, itemStats, entityStats);
|
return BukkitData.Statistics.from(genericStats, blockStats, itemStats, entityStats);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private Map<Statistic, Map<Material, Integer>> readMaterialStatistics(@NotNull JSONObject items) {
|
||||||
|
final Map<Statistic, Map<Material, Integer>> itemStats = Maps.newHashMap();
|
||||||
|
items.keys().forEachRemaining(stat -> {
|
||||||
|
final JSONObject itemStat = items.getJSONObject(stat);
|
||||||
|
final Map<Material, Integer> itemMap = Maps.newHashMap();
|
||||||
|
itemStat.keys().forEachRemaining(item -> {
|
||||||
|
final Material material = matchMaterial(item);
|
||||||
|
if (material != null) {
|
||||||
|
itemMap.put(material, itemStat.getInt(item));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
itemStats.put(matchStatistic(stat), itemMap);
|
||||||
|
});
|
||||||
|
return itemStats;
|
||||||
|
}
|
||||||
|
|
||||||
// Deserialize a legacy item stack array
|
// Deserialize a legacy item stack array
|
||||||
@NotNull
|
@NotNull
|
||||||
public ItemStack[] deserializeLegacyItemStacks(@NotNull String items) {
|
public ItemStack[] deserializeLegacyItemStacks(@NotNull String items) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
husksync {
|
husksync {
|
||||||
update;
|
update;
|
||||||
about;
|
about;
|
||||||
|
status;
|
||||||
reload;
|
reload;
|
||||||
}
|
}
|
||||||
@@ -18,7 +18,7 @@ dependencies {
|
|||||||
}
|
}
|
||||||
|
|
||||||
compileOnly 'net.kyori:adventure-api:4.15.0'
|
compileOnly 'net.kyori:adventure-api:4.15.0'
|
||||||
compileOnly 'net.kyori:adventure-platform-api:4.3.1'
|
compileOnly 'net.kyori:adventure-platform-api:4.3.2'
|
||||||
compileOnly 'org.jetbrains:annotations:24.1.0'
|
compileOnly 'org.jetbrains:annotations:24.1.0'
|
||||||
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"
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ package net.william278.husksync.command;
|
|||||||
|
|
||||||
import de.themoep.minedown.adventure.MineDown;
|
import de.themoep.minedown.adventure.MineDown;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.text.JoinConfiguration;
|
||||||
|
import net.kyori.adventure.text.event.HoverEvent;
|
||||||
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
import net.kyori.adventure.text.format.TextColor;
|
import net.kyori.adventure.text.format.TextColor;
|
||||||
import net.william278.desertwell.about.AboutMenu;
|
import net.william278.desertwell.about.AboutMenu;
|
||||||
import net.william278.desertwell.util.UpdateChecker;
|
import net.william278.desertwell.util.UpdateChecker;
|
||||||
@@ -28,10 +31,12 @@ import net.william278.husksync.HuskSync;
|
|||||||
import net.william278.husksync.migrator.Migrator;
|
import net.william278.husksync.migrator.Migrator;
|
||||||
import net.william278.husksync.user.CommandUser;
|
import net.william278.husksync.user.CommandUser;
|
||||||
import net.william278.husksync.user.OnlineUser;
|
import net.william278.husksync.user.OnlineUser;
|
||||||
|
import org.apache.commons.text.WordUtils;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.function.Function;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@@ -39,6 +44,7 @@ public class HuskSyncCommand extends Command implements TabProvider {
|
|||||||
|
|
||||||
private static final Map<String, Boolean> SUB_COMMANDS = Map.of(
|
private static final Map<String, Boolean> SUB_COMMANDS = Map.of(
|
||||||
"about", false,
|
"about", false,
|
||||||
|
"status", true,
|
||||||
"reload", true,
|
"reload", true,
|
||||||
"migrate", true,
|
"migrate", true,
|
||||||
"update", true
|
"update", true
|
||||||
@@ -92,6 +98,13 @@ public class HuskSyncCommand extends Command implements TabProvider {
|
|||||||
|
|
||||||
switch (subCommand) {
|
switch (subCommand) {
|
||||||
case "about" -> executor.sendMessage(aboutMenu.toComponent());
|
case "about" -> executor.sendMessage(aboutMenu.toComponent());
|
||||||
|
case "status" -> {
|
||||||
|
getPlugin().getLocales().getLocale("system_status_header").ifPresent(executor::sendMessage);
|
||||||
|
executor.sendMessage(Component.join(
|
||||||
|
JoinConfiguration.newlines(),
|
||||||
|
Arrays.stream(StatusLine.values()).map(s -> s.get(plugin)).toList()
|
||||||
|
));
|
||||||
|
}
|
||||||
case "reload" -> {
|
case "reload" -> {
|
||||||
try {
|
try {
|
||||||
plugin.loadConfigs();
|
plugin.loadConfigs();
|
||||||
@@ -182,4 +195,63 @@ public class HuskSyncCommand extends Command implements TabProvider {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private enum StatusLine {
|
||||||
|
PLUGIN_VERSION(plugin -> Component.text("v" + plugin.getPluginVersion().toStringWithoutMetadata())
|
||||||
|
.appendSpace().append(plugin.getPluginVersion().getMetadata().isBlank() ? Component.empty()
|
||||||
|
: Component.text("(build " + plugin.getPluginVersion().getMetadata() + ")"))),
|
||||||
|
PLATFORM_TYPE(plugin -> Component.text(WordUtils.capitalizeFully(plugin.getPlatformType()))),
|
||||||
|
LANGUAGE(plugin -> Component.text(plugin.getSettings().getLanguage())),
|
||||||
|
MINECRAFT_VERSION(plugin -> Component.text(plugin.getMinecraftVersion().toString())),
|
||||||
|
JAVA_VERSION(plugin -> Component.text(System.getProperty("java.version"))),
|
||||||
|
JAVA_VENDOR(plugin -> Component.text(System.getProperty("java.vendor"))),
|
||||||
|
SYNC_MODE(plugin -> Component.text(WordUtils.capitalizeFully(plugin.getSettings().getSyncMode().toString()))),
|
||||||
|
DELAY_LATENCY(plugin -> Component.text(plugin.getSettings().getNetworkLatencyMilliseconds() + "ms")),
|
||||||
|
SERVER_NAME(plugin -> Component.text(plugin.getServerName())),
|
||||||
|
DATABASE_TYPE(plugin -> Component.text(plugin.getSettings().getDatabaseType().getDisplayName())),
|
||||||
|
IS_DATABASE_LOCAL(plugin -> getLocalhostBoolean(plugin.getSettings().getMySqlHost())),
|
||||||
|
USING_REDIS_SENTINEL(plugin -> getBoolean(!plugin.getSettings().getRedisSentinelMaster().isBlank())),
|
||||||
|
USING_REDIS_PASSWORD(plugin -> getBoolean(!plugin.getSettings().getRedisPassword().isBlank())),
|
||||||
|
REDIS_USING_SSL(plugin -> getBoolean(plugin.getSettings().redisUseSsl())),
|
||||||
|
IS_REDIS_LOCAL(plugin -> getLocalhostBoolean(plugin.getSettings().getRedisHost())),
|
||||||
|
DATA_TYPES(plugin -> Component.join(
|
||||||
|
JoinConfiguration.commas(true),
|
||||||
|
plugin.getRegisteredDataTypes().stream().map(i -> {
|
||||||
|
boolean enabled = plugin.getSettings().isSyncFeatureEnabled(i);
|
||||||
|
return Component.textOfChildren(Component
|
||||||
|
.text(i.toString()).appendSpace().append(Component.text(enabled ? '✔' : '❌')))
|
||||||
|
.color(enabled ? NamedTextColor.GREEN : NamedTextColor.RED)
|
||||||
|
.hoverEvent(HoverEvent.showText(Component.text(enabled ? "Enabled" : "Disabled")));
|
||||||
|
}).toList()
|
||||||
|
));
|
||||||
|
|
||||||
|
private final Function<HuskSync, Component> supplier;
|
||||||
|
|
||||||
|
StatusLine(@NotNull Function<HuskSync, Component> supplier) {
|
||||||
|
this.supplier = supplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private Component get(@NotNull HuskSync plugin) {
|
||||||
|
return Component
|
||||||
|
.text("•").appendSpace()
|
||||||
|
.append(Component.text(
|
||||||
|
WordUtils.capitalizeFully(name().replaceAll("_", " ")),
|
||||||
|
TextColor.color(0x848484)
|
||||||
|
))
|
||||||
|
.append(Component.text(':')).append(Component.space().color(NamedTextColor.WHITE))
|
||||||
|
.append(supplier.apply(plugin));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private static Component getBoolean(boolean value) {
|
||||||
|
return Component.text(value ? "Yes" : "No", value ? NamedTextColor.GREEN : NamedTextColor.RED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private static Component getLocalhostBoolean(@NotNull String value) {
|
||||||
|
return getBoolean(value.equals("127.0.0.1") || value.equals("0.0.0.0")
|
||||||
|
|| value.equals("localhost") || value.equals("::1"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ public class DataDumper {
|
|||||||
} else {
|
} else {
|
||||||
return "(Failed to upload to logs site, got: " + connection.getResponseCode() + ")";
|
return "(Failed to upload to logs site, got: " + connection.getResponseCode() + ")";
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Throwable e) {
|
||||||
plugin.log(Level.SEVERE, "Failed to upload data to logs site", e);
|
plugin.log(Level.SEVERE, "Failed to upload data to logs site", e);
|
||||||
}
|
}
|
||||||
return "(Failed to upload to logs site)";
|
return "(Failed to upload to logs site)";
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ save_cause_converted_from_v2: 'converted from v2'
|
|||||||
up_to_date: '[HuskSync](#00fb9a bold) [| You are running the latest version of HuskSync (v%1%).](#00fb9a)'
|
up_to_date: '[HuskSync](#00fb9a bold) [| You are running the latest version of HuskSync (v%1%).](#00fb9a)'
|
||||||
update_available: '[HuskSync](#ff7e5e bold) [| A new version of HuskSync is available: v%1% (running: v%2%).](#ff7e5e)'
|
update_available: '[HuskSync](#ff7e5e bold) [| A new version of HuskSync is available: v%1% (running: v%2%).](#ff7e5e)'
|
||||||
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)'
|
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)'
|
||||||
|
system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)'
|
||||||
error_invalid_syntax: '[Грешка:](#ff3300) [Неправилен синтаксис. Използвайте:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)'
|
error_invalid_syntax: '[Грешка:](#ff3300) [Неправилен синтаксис. Използвайте:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)'
|
||||||
error_invalid_player: '[Грешка:](#ff3300) [Не можахме да открием играч с това име.](#ff7e5e)'
|
error_invalid_player: '[Грешка:](#ff3300) [Не можахме да открием играч с това име.](#ff7e5e)'
|
||||||
error_no_permission: '[Грешка:](#ff3300) [Нямате право да използвате тази команда](#ff7e5e)'
|
error_no_permission: '[Грешка:](#ff3300) [Нямате право да използвате тази команда](#ff7e5e)'
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ save_cause_converted_from_v2: 'Import von v2'
|
|||||||
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)'
|
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)'
|
||||||
up_to_date: '[HuskSync](#00fb9a bold) [| Du verwendest die neuste Version von HuskSync (v%1%).](#00fb9a)'
|
up_to_date: '[HuskSync](#00fb9a bold) [| Du verwendest die neuste Version von HuskSync (v%1%).](#00fb9a)'
|
||||||
update_available: '[HuskSync](#ff7e5e bold) [| Eine neue Version von HuskSync ist verfügbar: v%1% (Aktuelle Version: v%2%).](#ff7e5e)'
|
update_available: '[HuskSync](#ff7e5e bold) [| Eine neue Version von HuskSync ist verfügbar: v%1% (Aktuelle Version: v%2%).](#ff7e5e)'
|
||||||
|
system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)'
|
||||||
error_invalid_syntax: '[Fehler:](#ff3300) [Falsche Syntax. Nutze:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)'
|
error_invalid_syntax: '[Fehler:](#ff3300) [Falsche Syntax. Nutze:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)'
|
||||||
error_invalid_player: '[Fehler:](#ff3300) [Es konnte kein Spieler mit diesem Namen gefunden werden.](#ff7e5e)'
|
error_invalid_player: '[Fehler:](#ff3300) [Es konnte kein Spieler mit diesem Namen gefunden werden.](#ff7e5e)'
|
||||||
error_no_permission: '[Fehler:](#ff3300) [Du hast nicht die benötigten Berechtigungen um diesen Befehl auszuführen](#ff7e5e)'
|
error_no_permission: '[Fehler:](#ff3300) [Du hast nicht die benötigten Berechtigungen um diesen Befehl auszuführen](#ff7e5e)'
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ save_cause_converted_from_v2: 'converted from v2'
|
|||||||
up_to_date: '[HuskSync](#00fb9a bold) [| You are running the latest version of HuskSync (v%1%).](#00fb9a)'
|
up_to_date: '[HuskSync](#00fb9a bold) [| You are running the latest version of HuskSync (v%1%).](#00fb9a)'
|
||||||
update_available: '[HuskSync](#ff7e5e bold) [| A new version of HuskSync is available: v%1% (running: v%2%).](#ff7e5e)'
|
update_available: '[HuskSync](#ff7e5e bold) [| A new version of HuskSync is available: v%1% (running: v%2%).](#ff7e5e)'
|
||||||
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)'
|
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)'
|
||||||
|
system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)'
|
||||||
error_invalid_syntax: '[Error:](#ff3300) [Incorrect syntax. Usage:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)'
|
error_invalid_syntax: '[Error:](#ff3300) [Incorrect syntax. Usage:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)'
|
||||||
error_invalid_player: '[Error:](#ff3300) [Could not find a player by that name.](#ff7e5e)'
|
error_invalid_player: '[Error:](#ff3300) [Could not find a player by that name.](#ff7e5e)'
|
||||||
error_no_permission: '[Error:](#ff3300) [You do not have permission to execute this command](#ff7e5e)'
|
error_no_permission: '[Error:](#ff3300) [You do not have permission to execute this command](#ff7e5e)'
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ save_cause_converted_from_v2: 'converted from v2'
|
|||||||
up_to_date: '[HuskSync](#00fb9a bold) [| You are running the latest version of HuskSync (v%1%).](#00fb9a)'
|
up_to_date: '[HuskSync](#00fb9a bold) [| You are running the latest version of HuskSync (v%1%).](#00fb9a)'
|
||||||
update_available: '[HuskSync](#ff7e5e bold) [| A new version of HuskSync is available: v%1% (running: v%2%).](#ff7e5e)'
|
update_available: '[HuskSync](#ff7e5e bold) [| A new version of HuskSync is available: v%1% (running: v%2%).](#ff7e5e)'
|
||||||
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)'
|
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)'
|
||||||
|
system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)'
|
||||||
error_invalid_syntax: '[Error:](#ff3300) [Sintanxis incorrecta. Usa:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)'
|
error_invalid_syntax: '[Error:](#ff3300) [Sintanxis incorrecta. Usa:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)'
|
||||||
error_invalid_player: '[Error:](#ff3300) [No se ha podido encontrar un jugador con ese nombre.](#ff7e5e)'
|
error_invalid_player: '[Error:](#ff3300) [No se ha podido encontrar un jugador con ese nombre.](#ff7e5e)'
|
||||||
error_no_permission: '[Error:](#ff3300) [No tienes permisos para ejecutar este comando.](#ff7e5e)'
|
error_no_permission: '[Error:](#ff3300) [No tienes permisos para ejecutar este comando.](#ff7e5e)'
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ save_cause_converted_from_v2: 'converted from v2'
|
|||||||
up_to_date: '[HuskSync](#00fb9a bold) [| Il plugin è all''ultima versione disponibile (v%1%).](#00fb9a)'
|
up_to_date: '[HuskSync](#00fb9a bold) [| Il plugin è all''ultima versione disponibile (v%1%).](#00fb9a)'
|
||||||
update_available: '[HuskSync](#ff7e5e bold) [| Disponibile una nuova versione: v%1% (running: v%2%).](#ff7e5e)'
|
update_available: '[HuskSync](#ff7e5e bold) [| Disponibile una nuova versione: v%1% (running: v%2%).](#ff7e5e)'
|
||||||
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)'
|
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)'
|
||||||
|
system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)'
|
||||||
error_invalid_syntax: '[Errore:](#ff3300) [Sintassi errata. Usa:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)'
|
error_invalid_syntax: '[Errore:](#ff3300) [Sintassi errata. Usa:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)'
|
||||||
error_invalid_player: '[Errore:](#ff3300) [Impossibile trovare un giocatore con questo nome.](#ff7e5e)'
|
error_invalid_player: '[Errore:](#ff3300) [Impossibile trovare un giocatore con questo nome.](#ff7e5e)'
|
||||||
error_no_permission: '[Errore:](#ff3300) [Non hai il permesso di usare questo comando](#ff7e5e)'
|
error_no_permission: '[Errore:](#ff3300) [Non hai il permesso di usare questo comando](#ff7e5e)'
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ save_cause_converted_from_v2: 'converted from v2'
|
|||||||
up_to_date: '[HuskSync](#00fb9a bold) [| HuskSyncの最新バージョンを実行しています(v%1%).](#00fb9a)'
|
up_to_date: '[HuskSync](#00fb9a bold) [| HuskSyncの最新バージョンを実行しています(v%1%).](#00fb9a)'
|
||||||
update_available: '[HuskSync](#ff7e5e bold) [| HuskSyncの最新バージョンが更新されています: v%1% (実行中: v%2%).](#ff7e5e)'
|
update_available: '[HuskSync](#ff7e5e bold) [| HuskSyncの最新バージョンが更新されています: v%1% (実行中: v%2%).](#ff7e5e)'
|
||||||
reload_complete: '[HuskSync](#00fb9a bold) [| 設定ファイルとメッセージファイルを再読み込みしました。](#00fb9a)\n[⚠ すべてのサーバーで設定ファイルが最新であることを確認してください!](#00fb9a)\n[設定の変更を有効にするには再起動が必要です。](#00fb9a italic)'
|
reload_complete: '[HuskSync](#00fb9a bold) [| 設定ファイルとメッセージファイルを再読み込みしました。](#00fb9a)\n[⚠ すべてのサーバーで設定ファイルが最新であることを確認してください!](#00fb9a)\n[設定の変更を有効にするには再起動が必要です。](#00fb9a italic)'
|
||||||
|
system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)'
|
||||||
error_invalid_syntax: '[Error:](#ff3300) [構文が正しくありません。使用法:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&クリックでサジェスト suggest_command=%1%)'
|
error_invalid_syntax: '[Error:](#ff3300) [構文が正しくありません。使用法:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&クリックでサジェスト suggest_command=%1%)'
|
||||||
error_invalid_player: '[Error:](#ff3300) [そのプレイヤーは見つかりませんでした](#ff7e5e)'
|
error_invalid_player: '[Error:](#ff3300) [そのプレイヤーは見つかりませんでした](#ff7e5e)'
|
||||||
error_no_permission: '[Error:](#ff3300) [このコマンドを実行する権限がありません](#ff7e5e)'
|
error_no_permission: '[Error:](#ff3300) [このコマンドを実行する権限がありません](#ff7e5e)'
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ save_cause_converted_from_v2: 'converted from v2'
|
|||||||
up_to_date: '[HuskSync](#00fb9a bold) [| 가장 최신 버전의 HuskSync를 실행 중입니다 (v%1%).](#00fb9a)'
|
up_to_date: '[HuskSync](#00fb9a bold) [| 가장 최신 버전의 HuskSync를 실행 중입니다 (v%1%).](#00fb9a)'
|
||||||
update_available: '[HuskSync](#ff7e5e bold) [| 새로운 버전의 HuskSync가 존재합니다: v%1% (현재 버전: v%2%).](#ff7e5e)'
|
update_available: '[HuskSync](#ff7e5e bold) [| 새로운 버전의 HuskSync가 존재합니다: v%1% (현재 버전: v%2%).](#ff7e5e)'
|
||||||
reload_complete: '[HuskSync](#00fb9a bold) [| 콘피그와 메시지 파일을 다시 불러왔습니다.](#00fb9a)\n[⚠ 모든 서버의 컨피그 파일을 변경하였는지 확인하세요!](#00fb9a)\n[몇몇 설정은 재시작 후에 적용됩니다.](#00fb9a italic)'
|
reload_complete: '[HuskSync](#00fb9a bold) [| 콘피그와 메시지 파일을 다시 불러왔습니다.](#00fb9a)\n[⚠ 모든 서버의 컨피그 파일을 변경하였는지 확인하세요!](#00fb9a)\n[몇몇 설정은 재시작 후에 적용됩니다.](#00fb9a italic)'
|
||||||
|
system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)'
|
||||||
error_invalid_syntax: '[오류:](#ff3300) [잘못된 사용법. 사용법:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&클릭하여 입력할 수 있습니다. suggest_command=%1%)'
|
error_invalid_syntax: '[오류:](#ff3300) [잘못된 사용법. 사용법:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&클릭하여 입력할 수 있습니다. suggest_command=%1%)'
|
||||||
error_invalid_player: '[오류:](#ff3300) [해당 이름의 사용자를 찾을 수 없습니다.](#ff7e5e)'
|
error_invalid_player: '[오류:](#ff3300) [해당 이름의 사용자를 찾을 수 없습니다.](#ff7e5e)'
|
||||||
error_no_permission: '[오류:](#ff3300) [해당 명령어를 사용할 권한이 없습니다.](#ff7e5e)'
|
error_no_permission: '[오류:](#ff3300) [해당 명령어를 사용할 권한이 없습니다.](#ff7e5e)'
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ save_cause_converted_from_v2: 'converted from v2'
|
|||||||
up_to_date: '[HuskSync](#00fb9a bold) [| Je gebruikt de nieuwste versie van HuskSync (v%1%).](#00fb9a)'
|
up_to_date: '[HuskSync](#00fb9a bold) [| Je gebruikt de nieuwste versie van HuskSync (v%1%).](#00fb9a)'
|
||||||
update_available: '[HuskSync](#ff7e5e bold) [| Er is een nieuwe versie van HuskSync beschikbaar: v%1% (huidige versie: v%2%).](#ff7e5e)'
|
update_available: '[HuskSync](#ff7e5e bold) [| Er is een nieuwe versie van HuskSync beschikbaar: v%1% (huidige versie: v%2%).](#ff7e5e)'
|
||||||
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)'
|
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)'
|
||||||
|
system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)'
|
||||||
error_invalid_syntax: '[Error:](#ff3300) [Onjuiste syntaxis. Gebruik:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)'
|
error_invalid_syntax: '[Error:](#ff3300) [Onjuiste syntaxis. Gebruik:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)'
|
||||||
error_invalid_player: '[Error:](#ff3300) [Kan geen speler met die naam vinden.](#ff7e5e)'
|
error_invalid_player: '[Error:](#ff3300) [Kan geen speler met die naam vinden.](#ff7e5e)'
|
||||||
error_no_permission: '[Error:](#ff3300) [Je hebt geen toestemming om deze opdracht uit te voeren](#ff7e5e)'
|
error_no_permission: '[Error:](#ff3300) [Je hebt geen toestemming om deze opdracht uit te voeren](#ff7e5e)'
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ save_cause_converted_from_v2: 'converted from v2'
|
|||||||
up_to_date: '[HuskSync](#00fb9a bold) [| You are running the latest version of HuskSync (v%1%).](#00fb9a)'
|
up_to_date: '[HuskSync](#00fb9a bold) [| You are running the latest version of HuskSync (v%1%).](#00fb9a)'
|
||||||
update_available: '[HuskSync](#ff7e5e bold) [| A new version of HuskSync is available: v%1% (running: v%2%).](#ff7e5e)'
|
update_available: '[HuskSync](#ff7e5e bold) [| A new version of HuskSync is available: v%1% (running: v%2%).](#ff7e5e)'
|
||||||
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)'
|
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)'
|
||||||
|
system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)'
|
||||||
error_invalid_syntax: '[Error:](#ff3300) [Sintaxe incorreta. Utilize:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)'
|
error_invalid_syntax: '[Error:](#ff3300) [Sintaxe incorreta. Utilize:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)'
|
||||||
error_invalid_player: '[Error:](#ff3300) [Não foi possível encontrar um jogador com esse nome.](#ff7e5e)'
|
error_invalid_player: '[Error:](#ff3300) [Não foi possível encontrar um jogador com esse nome.](#ff7e5e)'
|
||||||
error_no_permission: '[Error:](#ff3300) [Você não tem permissão para executar este comando](#ff7e5e)'
|
error_no_permission: '[Error:](#ff3300) [Você não tem permissão para executar este comando](#ff7e5e)'
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ save_cause_converted_from_v2: 'конвертация с v2'
|
|||||||
up_to_date: '[HuskSync](#00fb9a bold) [| Вы используете последнюю версию HuskSync (v%1%).](#00fb9a)'
|
up_to_date: '[HuskSync](#00fb9a bold) [| Вы используете последнюю версию HuskSync (v%1%).](#00fb9a)'
|
||||||
update_available: '[HuskSync](#ff7e5e bold) [| Доступна новая версия HuskSync: v%1% (текущая: v%2%).](#ff7e5e)'
|
update_available: '[HuskSync](#ff7e5e bold) [| Доступна новая версия HuskSync: v%1% (текущая: v%2%).](#ff7e5e)'
|
||||||
reload_complete: '[HuskSync](#00fb9a bold) [| Конфигурация и файлы локализации перезагружены.](#00fb9a)\n[⚠ Убедитесь, что файлы конфигурации обновлены на всех серверах!](#00fb9a)\n[Необходима перезагрузка для вступления изменений конфигурации в силу.](#00fb9a italic)'
|
reload_complete: '[HuskSync](#00fb9a bold) [| Конфигурация и файлы локализации перезагружены.](#00fb9a)\n[⚠ Убедитесь, что файлы конфигурации обновлены на всех серверах!](#00fb9a)\n[Необходима перезагрузка для вступления изменений конфигурации в силу.](#00fb9a italic)'
|
||||||
|
system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)'
|
||||||
error_invalid_syntax: '[Ошибка:](#ff3300) [Неправильный синтаксис. Используйте:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)'
|
error_invalid_syntax: '[Ошибка:](#ff3300) [Неправильный синтаксис. Используйте:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)'
|
||||||
error_invalid_player: '[Ошибка:](#ff3300) [Не удалось найти игрока с данным именем.](#ff7e5e)'
|
error_invalid_player: '[Ошибка:](#ff3300) [Не удалось найти игрока с данным именем.](#ff7e5e)'
|
||||||
error_no_permission: '[Ошибка:](#ff3300) [У вас недостаточно прав для выполнения данной команды.](#ff7e5e)'
|
error_no_permission: '[Ошибка:](#ff3300) [У вас недостаточно прав для выполнения данной команды.](#ff7e5e)'
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ save_cause_converted_from_v2: 'converted from v2'
|
|||||||
up_to_date: '[HuskSync](#00fb9a bold) [| HuskSync\''in en son sürümünü kullanıyorsunuz (v%1%).](#00fb9a)'
|
up_to_date: '[HuskSync](#00fb9a bold) [| HuskSync\''in en son sürümünü kullanıyorsunuz (v%1%).](#00fb9a)'
|
||||||
update_available: '[HuskSync](#ff7e5e bold) [| HuskSync\''in yeni bir sürümü mevcut: v%1% (kullanılan sürüm: v%2%).](#ff7e5e)'
|
update_available: '[HuskSync](#ff7e5e bold) [| HuskSync\''in yeni bir sürümü mevcut: v%1% (kullanılan sürüm: v%2%).](#ff7e5e)'
|
||||||
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)'
|
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)'
|
||||||
|
system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)'
|
||||||
error_invalid_syntax: '[Hata:](#ff3300) [Yanlış sözdizimi. Kullanım:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Öneri için tıklayın Suggest_command=%1%)'
|
error_invalid_syntax: '[Hata:](#ff3300) [Yanlış sözdizimi. Kullanım:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Öneri için tıklayın Suggest_command=%1%)'
|
||||||
error_invalid_player: '[Hata:](#ff3300) [Bu isimde bir oyuncu bulunamadı.](#ff7e5e)'
|
error_invalid_player: '[Hata:](#ff3300) [Bu isimde bir oyuncu bulunamadı.](#ff7e5e)'
|
||||||
error_no_permission: '[Hata:](#ff3300) [Bu komutu gerçekleştirmek için izniniz yok](#ff7e5e)'
|
error_no_permission: '[Hata:](#ff3300) [Bu komutu gerçekleştirmek için izniniz yok](#ff7e5e)'
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ save_cause_converted_from_v2: 'converted from v2'
|
|||||||
up_to_date: '[HuskSync](#00fb9a bold) [| You are running the latest version of HuskSync (v%1%).](#00fb9a)'
|
up_to_date: '[HuskSync](#00fb9a bold) [| You are running the latest version of HuskSync (v%1%).](#00fb9a)'
|
||||||
update_available: '[HuskSync](#ff7e5e bold) [| A new version of HuskSync is available: v%1% (running: v%2%).](#ff7e5e)'
|
update_available: '[HuskSync](#ff7e5e bold) [| A new version of HuskSync is available: v%1% (running: v%2%).](#ff7e5e)'
|
||||||
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)'
|
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)'
|
||||||
|
system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)'
|
||||||
error_invalid_syntax: '[Помилка:](#ff3300) [Неправильний синтакс. Використання:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)'
|
error_invalid_syntax: '[Помилка:](#ff3300) [Неправильний синтакс. Використання:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)'
|
||||||
error_invalid_player: '[Помилка:](#ff3300) [Гравця не знайдено](#ff7e5e)'
|
error_invalid_player: '[Помилка:](#ff3300) [Гравця не знайдено](#ff7e5e)'
|
||||||
error_no_permission: '[Помилка:](#ff3300) [Ввас немає дозволу на використання цієї команди](#ff7e5e)'
|
error_no_permission: '[Помилка:](#ff3300) [Ввас немає дозволу на використання цієї команди](#ff7e5e)'
|
||||||
|
|||||||
@@ -34,20 +34,21 @@ list_page_jumper_button: '[%1%](show_text=&7跳转到页面 %1% run_command=%2%
|
|||||||
list_page_jumper_current_page: '[%1%](#00fb9a)'
|
list_page_jumper_current_page: '[%1%](#00fb9a)'
|
||||||
list_page_jumper_separator: ' '
|
list_page_jumper_separator: ' '
|
||||||
list_page_jumper_group_separator: '…'
|
list_page_jumper_group_separator: '…'
|
||||||
save_cause_disconnect: 'disconnect'
|
save_cause_disconnect: '断开连接'
|
||||||
save_cause_world_save: 'world save'
|
save_cause_world_save: '保存世界'
|
||||||
save_cause_death: 'death'
|
save_cause_death: '死亡'
|
||||||
save_cause_server_shutdown: 'server shutdown'
|
save_cause_server_shutdown: '服务器关闭'
|
||||||
save_cause_inventory_command: 'inventory command'
|
save_cause_inventory_command: '背包命令'
|
||||||
save_cause_enderchest_command: 'enderchest command'
|
save_cause_enderchest_command: '末影箱命令'
|
||||||
save_cause_backup_restore: 'backup restore'
|
save_cause_backup_restore: '备份还原'
|
||||||
save_cause_api: 'API'
|
save_cause_api: 'API'
|
||||||
save_cause_mpdb_migration: 'MPDB migration'
|
save_cause_mpdb_migration: 'MPDB迁移'
|
||||||
save_cause_legacy_migration: 'legacy migration'
|
save_cause_legacy_migration: '旧版迁移'
|
||||||
save_cause_converted_from_v2: 'converted from v2'
|
save_cause_converted_from_v2: '从v2转换'
|
||||||
up_to_date: '[HuskSync](#00fb9a bold) [| 你正在使用最新版本的HuskSync (v%1%)](#00fb9a)'
|
up_to_date: '[HuskSync](#00fb9a bold) [| 你正在使用最新版本的HuskSync (v%1%)](#00fb9a)'
|
||||||
update_available: '[HuskSync](#ff7e5e bold) [| 一个新版本的HuskSync已经可以更新: v%1% (当前: v%2%)](#ff7e5e)'
|
update_available: '[HuskSync](#ff7e5e bold) [| 一个新版本的HuskSync已经可以更新: v%1% (当前: v%2%)](#ff7e5e)'
|
||||||
reload_complete: '[HuskSync](#00fb9a bold) [| 插件配置和语言文件已重载.](#00fb9a)\n[⚠ 确保所有服务器上的配置文件都是最新的!](#00fb9a)\n[需要重新启动配置更改才能生效.](#00fb9a italic)'
|
reload_complete: '[HuskSync](#00fb9a bold) [| 插件配置和语言文件已重载.](#00fb9a)\n[⚠ 确保所有服务器上的配置文件都是最新的!](#00fb9a)\n[需要重新启动配置更改才能生效.](#00fb9a italic)'
|
||||||
|
system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)'
|
||||||
error_invalid_syntax: ':](#ff3300) [格式错误, 使用方法:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&点击建议 suggest_command=%1%)'
|
error_invalid_syntax: ':](#ff3300) [格式错误, 使用方法:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&点击建议 suggest_command=%1%)'
|
||||||
error_invalid_player: '[错误:](#ff3300) [无法找到目标玩家.](#ff7e5e)'
|
error_invalid_player: '[错误:](#ff3300) [无法找到目标玩家.](#ff7e5e)'
|
||||||
error_no_permission: '[错误:](#ff3300) [你没有执行此指令的权限](#ff7e5e)'
|
error_no_permission: '[错误:](#ff3300) [你没有执行此指令的权限](#ff7e5e)'
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ save_cause_converted_from_v2: 'converted from v2'
|
|||||||
up_to_date: '[HuskSync](#00fb9a bold) [| 您運行的是最新版本的 HuskSync (v%1%).](#00fb9a)'
|
up_to_date: '[HuskSync](#00fb9a bold) [| 您運行的是最新版本的 HuskSync (v%1%).](#00fb9a)'
|
||||||
update_available: '[HuskSync](#ff7e5e bold) [| 發現可用的新版本: v%1% (running: v%2%).](#ff7e5e)'
|
update_available: '[HuskSync](#ff7e5e bold) [| 發現可用的新版本: v%1% (running: v%2%).](#ff7e5e)'
|
||||||
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)'
|
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)'
|
||||||
|
system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)'
|
||||||
error_invalid_syntax: '[錯誤:](#ff3300) [語法不正確,用法:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)'
|
error_invalid_syntax: '[錯誤:](#ff3300) [語法不正確,用法:](#ff7e5e) [%1%](#ff7e5e italic show_text=&#ff7e5e&Click to suggest suggest_command=%1%)'
|
||||||
error_invalid_player: '[錯誤:](#ff3300) [找不到這位玩家](#ff7e5e)'
|
error_invalid_player: '[錯誤:](#ff3300) [找不到這位玩家](#ff7e5e)'
|
||||||
error_no_permission: '[錯誤:](#ff3300) [您沒有權限執行這個指令](#ff7e5e)'
|
error_no_permission: '[錯誤:](#ff3300) [您沒有權限執行這個指令](#ff7e5e)'
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ This page contains a table of HuskSync commands and their required permission no
|
|||||||
<tbody>
|
<tbody>
|
||||||
<!-- /husksync command -->
|
<!-- /husksync command -->
|
||||||
<tr>
|
<tr>
|
||||||
<td rowspan="5"><code>/husksync</code></td>
|
<td rowspan="6"><code>/husksync</code></td>
|
||||||
<td><code>/husksync</code></td>
|
<td><code>/husksync</code></td>
|
||||||
<td>View & manage plugin system information</td>
|
<td>View & manage plugin system information</td>
|
||||||
<td><code>husksync.command.husksync</code></td>
|
<td><code>husksync.command.husksync</code></td>
|
||||||
@@ -21,6 +21,11 @@ This page contains a table of HuskSync commands and their required permission no
|
|||||||
<td>View information about the plugin</td>
|
<td>View information about the plugin</td>
|
||||||
<td><code>husksync.command.husksync.about</code></td>
|
<td><code>husksync.command.husksync.about</code></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>/husksync status</code></td>
|
||||||
|
<td>View plugin system status information</td>
|
||||||
|
<td><code>husksync.command.husksync.status</code></td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><code>/husksync reload</code></td>
|
<td><code>/husksync reload</code></td>
|
||||||
<td>Reload the plugin configuration</td>
|
<td>Reload the plugin configuration</td>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ HuskSync offers two built-in **synchronization modes** that utilise Redis and My
|
|||||||

|

|
||||||
|
|
||||||
## Available Modes
|
## Available Modes
|
||||||
* The `LOCKSTEP` sync mode is the default sync mode. It uses a data checkout system to ensure that all servers are in sync regardless of network latency or tick rate fluctuations. This mode was introduced in HuskSync v3.1
|
* The `LOCKSTEP` sync mode is the default sync mode. It uses a data checkout system to ensure that all servers are in sync regardless of network latency or tick rate fluctuations. This mode was introduced in HuskSync v3.1, and was made the default in v3.2.
|
||||||
* The `DELAY` sync mode uses the `network_latency_miliseconds` value to apply a delay before listening to Redis data
|
* The `DELAY` sync mode uses the `network_latency_miliseconds` value to apply a delay before listening to Redis data
|
||||||
|
|
||||||
You can change which sync mode you are using by editing the `sync_mode` setting under `synchronization` in `config.yml`.
|
You can change which sync mode you are using by editing the `sync_mode` setting under `synchronization` in `config.yml`.
|
||||||
@@ -31,6 +31,8 @@ The `LOCKSTEP` sync mode works as described below:
|
|||||||
|
|
||||||
Additionally, note that `DATA_CHECKOUT` keys are set with the server ID of the server which "checked out" the data (taken from the `server.yml` config file). On both shutdown and startup, the plugin will clear all `DATA_CHECKOUT` keys for the current server ID (to prevent stale keys in the event of a server crash for instance)
|
Additionally, note that `DATA_CHECKOUT` keys are set with the server ID of the server which "checked out" the data (taken from the `server.yml` config file). On both shutdown and startup, the plugin will clear all `DATA_CHECKOUT` keys for the current server ID (to prevent stale keys in the event of a server crash for instance)
|
||||||
|
|
||||||
|
`LOCKSTEP` has been the default sync mode since HuskSync v3.2, and is recommended for most networks.
|
||||||
|
|
||||||
## Delay
|
## Delay
|
||||||
The `DELAY` sync mode works as described below:
|
The `DELAY` sync mode works as described below:
|
||||||
* When a user disconnects from a server, a `SERVER_SWITCH` key is immediately set on Redis, followed by a `DATA_UPDATE` key which contains the user's packed and serialized Data Snapshot.
|
* When a user disconnects from a server, a `SERVER_SWITCH` key is immediately set on Redis, followed by a `DATA_UPDATE` key which contains the user's packed and serialized Data Snapshot.
|
||||||
@@ -40,6 +42,4 @@ The `DELAY` sync mode works as described below:
|
|||||||
* If present, it will continuously attempt to read for a `DATA_UPDATE` key; when read, their data will be set from the snapshot deserialized from Redis.
|
* If present, it will continuously attempt to read for a `DATA_UPDATE` key; when read, their data will be set from the snapshot deserialized from Redis.
|
||||||
* If not present, their data will be pulled from the database (as though they joined the network)
|
* If not present, their data will be pulled from the database (as though they joined the network)
|
||||||
|
|
||||||
`DELAY` has been the default sync mode since HuskSync v2.0. In HuskSync v3.1, `LOCKSTEP` was introduced. Since the delay mode has been tested and deployed for the longest, it is still the default, though note this may change in the future.
|
If your network has a fluctuating tick rate or significant latency (especially if you have servers on different hardware/locations), you may wish to use `LOCKSTEP` instead for a more reliable sync system.
|
||||||
|
|
||||||
However, if your network has a fluctuating tick rate or significant latency (especially if you have servers on different hardware/locations), you may wish to use `LOCKSTEP` instead for a more reliable sync system.
|
|
||||||
@@ -3,7 +3,7 @@ org.gradle.jvmargs='-Dfile.encoding=UTF-8'
|
|||||||
org.gradle.daemon=true
|
org.gradle.daemon=true
|
||||||
javaVersion=16
|
javaVersion=16
|
||||||
|
|
||||||
plugin_version=3.2
|
plugin_version=3.2.1
|
||||||
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
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user