Skip to content

Commit e56041e

Browse files
committed
feat: add /userdata save
Also adds docs for `/husksync dump`
1 parent 904c65b commit e56041e

21 files changed

+85
-7
lines changed

common/src/main/java/net/william278/husksync/command/PluginCommand.java

+14
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.mojang.brigadier.exceptions.CommandSyntaxException;
2424
import net.william278.husksync.HuskSync;
2525
import net.william278.husksync.user.CommandUser;
26+
import net.william278.husksync.user.OnlineUser;
2627
import net.william278.husksync.user.User;
2728
import net.william278.uniform.BaseCommand;
2829
import net.william278.uniform.Command;
@@ -75,6 +76,19 @@ protected CommandUser adapt(net.william278.uniform.CommandUser user) {
7576
return user.getUuid() == null ? plugin.getConsole() : plugin.getOnlineUser(user.getUuid()).orElseThrow();
7677
}
7778

79+
@NotNull
80+
protected <S> ArgumentElement<S, OnlineUser> onlineUser(@NotNull String name) {
81+
return new ArgumentElement<>(name, reader -> {
82+
final String username = reader.readString();
83+
return plugin.getOnlineUsers().stream()
84+
.filter(user -> username.equals(user.getName()))
85+
.findFirst().orElse(null);
86+
}, (context, builder) -> {
87+
plugin.getOnlineUsers().forEach(u -> builder.suggest(u.getName()));
88+
return builder.buildFuture();
89+
});
90+
}
91+
7892
@NotNull
7993
protected <S> ArgumentElement<S, User> user(@NotNull String name) {
8094
return new ArgumentElement<>(name, reader -> {

common/src/main/java/net/william278/husksync/command/UserDataCommand.java

+17
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import net.william278.husksync.redis.RedisKeyType;
2626
import net.william278.husksync.redis.RedisManager;
2727
import net.william278.husksync.user.CommandUser;
28+
import net.william278.husksync.user.OnlineUser;
2829
import net.william278.husksync.user.User;
2930
import net.william278.husksync.util.UserDataDumper;
3031
import net.william278.husksync.util.DataSnapshotList;
@@ -52,6 +53,7 @@ public void provide(@NotNull BaseCommand<?> command) {
5253
command.addSubCommand("view", needsOp("view"), view());
5354
command.addSubCommand("list", needsOp("list"), list());
5455
command.addSubCommand("delete", needsOp("delete"), delete());
56+
command.addSubCommand("save", needsOp("save"), save());
5557
command.addSubCommand("restore", needsOp("restore"), restore());
5658
command.addSubCommand("pin", needsOp("pin"), pin());
5759
command.addSubCommand("dump", needsOp("dump"), dump());
@@ -102,6 +104,13 @@ private void listSnapshots(@NotNull CommandUser executor, @NotNull User user, in
102104
DataSnapshotList.create(dataList, user, plugin).displayPage(executor, page);
103105
}
104106

107+
// Create and save a snapshot of a user's current data
108+
private void createAndSaveSnapshot(@NotNull CommandUser executor, @NotNull OnlineUser onlineUser) {
109+
plugin.getDataSyncer().saveCurrentUserData(onlineUser, DataSnapshot.SaveCause.SAVE_COMMAND);
110+
plugin.getLocales().getLocale("data_saved", onlineUser.getName())
111+
.ifPresent(executor::sendMessage);
112+
}
113+
105114
// Delete a snapshot
106115
private void deleteSnapshot(@NotNull CommandUser executor, @NotNull User user, @NotNull UUID version) {
107116
if (!plugin.getDatabase().deleteSnapshot(user, version)) {
@@ -234,6 +243,14 @@ private CommandProvider delete() {
234243
}, user("username"), uuid("version"));
235244
}
236245

246+
@NotNull
247+
private CommandProvider save() {
248+
return (sub) -> sub.addSyntax((ctx) -> {
249+
final OnlineUser user = ctx.getArgument("username", OnlineUser.class);
250+
createAndSaveSnapshot(user(sub, ctx), user);
251+
}, onlineUser("username"));
252+
}
253+
237254
@NotNull
238255
private CommandProvider restore() {
239256
return (sub) -> sub.addSyntax((ctx) -> {

common/src/main/java/net/william278/husksync/data/DataSnapshot.java

+11-4
Original file line numberDiff line numberDiff line change
@@ -535,9 +535,9 @@ public Builder pinned(boolean pinned) {
535535
public Builder timestamp(@NotNull OffsetDateTime timestamp) {
536536
if (timestamp.isAfter(OffsetDateTime.now())) {
537537
throw new IllegalArgumentException("Data snapshots cannot have a timestamp set in the future! "
538-
+ "Make sure your database server time matches the server time.\n"
539-
+ "Current game server timestamp: " + OffsetDateTime.now() + " / "
540-
+ "Snapshot timestamp: " + timestamp);
538+
+ "Make sure your database server time matches the server time.\n"
539+
+ "Current game server timestamp: " + OffsetDateTime.now() + " / "
540+
+ "Snapshot timestamp: " + timestamp);
541541
}
542542
this.timestamp = timestamp;
543543
return this;
@@ -880,6 +880,13 @@ public static class SaveCause implements Cause {
880880
*/
881881
public static final SaveCause BACKUP_RESTORE = of("BACKUP_RESTORE");
882882

883+
/**
884+
* Indicates data was saved from executing the {@code /userdata save} command
885+
*
886+
* @since 3.8
887+
*/
888+
public static final SaveCause SAVE_COMMAND = of("SAVE_COMMAND", true);
889+
883890
/**
884891
* Indicates data was saved by an API call
885892
*
@@ -923,7 +930,7 @@ public static class SaveCause implements Cause {
923930
*/
924931
@NotNull
925932
public static SaveCause of(@NotNull String name) {
926-
return of(name,true);
933+
return of(name, true);
927934
}
928935

929936
/**

common/src/main/java/net/william278/husksync/sync/DataSyncer.java

+15-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import net.william278.husksync.api.HuskSyncAPI;
2424
import net.william278.husksync.data.DataSnapshot;
2525
import net.william278.husksync.database.Database;
26+
import net.william278.husksync.redis.RedisKeyType;
2627
import net.william278.husksync.redis.RedisManager;
2728
import net.william278.husksync.user.OnlineUser;
2829
import net.william278.husksync.user.User;
@@ -94,6 +95,19 @@ public void terminate() {
9495
*/
9596
public abstract void syncSaveUserData(@NotNull OnlineUser user);
9697

98+
/**
99+
* Save a user's current data
100+
*
101+
* @param onlineUser the user to save data of
102+
* @param cause the save cause
103+
*/
104+
public void saveCurrentUserData(@NotNull OnlineUser onlineUser, @NotNull DataSnapshot.SaveCause cause) {
105+
this.saveData(
106+
onlineUser, onlineUser.createSnapshot(cause),
107+
(user, data) -> getRedis().setUserData(user, data, RedisKeyType.TTL_10_SECONDS)
108+
);
109+
}
110+
97111
/**
98112
* Save a {@link DataSnapshot.Packed user's data snapshot} to the database,
99113
* first firing the {@link net.william278.husksync.event.DataSaveEvent}. This will not update data on Redis.
@@ -150,7 +164,7 @@ private void addSnapshotToDatabase(@NotNull User user, @NotNull DataSnapshot.Pac
150164
private long getMaxListenAttempts() {
151165
return BASE_LISTEN_ATTEMPTS + (
152166
(Math.max(100, plugin.getSettings().getSynchronization().getNetworkLatencyMilliseconds()) / 1000)
153-
* 20 / LISTEN_DELAY
167+
* 20 / LISTEN_DELAY
154168
);
155169
}
156170

common/src/main/resources/locales/bg-bg.yml

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ locales:
2323
data_list_title: '[Лист от](#00fb9a) [снапшоти на данните на потребителя](#00fb9a) [%1%](#00fb9a bold show_text=&7UUID: %2%)\n'
2424
data_list_item: '[%1%](gray show_text=&7User Data Snapshot for %2%\n&8⚡ %4% run_command=/userdata view %2% %3%) [%5%](#d8ff2b show_text=&7Pinned:\n&8Pinned snapshots won''t be automatically rotated. run_command=/userdata view %2% %3%) [%6%](color=#ffc43b-#f5c962 show_text=&7Version timestamp:&7\n&8When the data was saved\n&8%7% run_command=/userdata view %2% %3%) [⚑ %8%](#23a825-#36f539 show_text=&7Save cause:\n&8What caused the data to be saved run_command=/userdata view %2% %3%) [⏏ %9%](color=#62a9f5-#7ab8fa show_text=&7Snapshot size:&7\n&8Estimated file size of the snapshot (in KiB) run_command=/userdata view %2% %3%)'
2525
data_list_item_invalid: '[%1%](dark_gray show_text=&7User Data Snapshot for %2%\n&8⚡ %4% suggest_command=/userdata delete %2% %3%) [%5%](dark_gray show_text=&7Pinned:\n&8Pinned snapshots won''t be automatically rotated. suggest_command=/userdata delete %2% %3%) [%6% ⚑ %8% ⏏ %9%](gray strikethrough show_text=&#ff3300&Invalid Data Snapshot\n&#ff7e5e&Click to delete\n\n&7⚠ %10% suggest_command=/userdata delete %2% %3%)'
26+
data_saved: '[Successfully saved a snapshot of %1%''s current user data.](#00fb9a)'
2627
data_deleted: '[❌ Успешно изтрихме снапшота с потребителски данни](#00fb9a) [%1%](#00fb9a show_text=&7Версия на UUID:\n&8%2%) [за](#00fb9a) [%3%.](#00fb9a show_text=&7UUID на Играча:\n&8%4%)'
2728
data_restored: '[⏪ Успешно възстановихме](#00fb9a) [текущите потребителски данни за](#00fb9a) [%1%](#00fb9a show_text=&7UUID на Играча:\n&8%2%) [от снапшот](#00fb9a) [%3%.](#00fb9a show_text=&7Версия на UUID:\n&8%4%)'
2829
data_pinned: '[※ Успешно закачихме снапшота с потребителски данни](#00fb9a) [%1%](#00fb9a show_text=&7Версия на UUID:\n&8%2%) [за](#00fb9a) [%3%.](#00fb9a show_text=&7UUID на Играча:\n&8%4%)'

common/src/main/resources/locales/de-de.yml

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ locales:
2323
data_list_title: '[Nutzerdaten-Schnappschüsse von %1%:](#00fb9a) [(%2%-%3% von](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n'
2424
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%)'
2525
data_list_item_invalid: '[%1%](dark_gray show_text=&7User Data Snapshot for %2%\n&8⚡ %4% suggest_command=/userdata delete %2% %3%) [%5%](dark_gray show_text=&7Pinned:\n&8Pinned snapshots won''t be automatically rotated. suggest_command=/userdata delete %2% %3%) [%6% ⚑ %8% ⏏ %9%](gray strikethrough show_text=&#ff3300&Invalid Data Snapshot\n&#ff7e5e&Click to delete\n\n&7⚠ %10% suggest_command=/userdata delete %2% %3%)'
26+
data_saved: '[Successfully saved a snapshot of %1%''s current user data.](#00fb9a)'
2627
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%)'
2728
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%)'
2829
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%)'

common/src/main/resources/locales/en-gb.yml

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ locales:
2323
data_list_title: '[%1%''s user data snapshots:](#00fb9a) [(%2%-%3% of](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n'
2424
data_list_item: '[%1%](gray show_text=&7User Data Snapshot for %2%\n&8⚡ %4% run_command=/userdata view %2% %3%) [%5%](#d8ff2b show_text=&7Pinned:\n&8Pinned snapshots won''t be automatically rotated. run_command=/userdata view %2% %3%) [%6%](color=#ffc43b-#f5c962 show_text=&7Version timestamp:&7\n&8When the data was saved\n&8%7% run_command=/userdata view %2% %3%) [⚑ %8%](#23a825-#36f539 show_text=&7Save cause:\n&8What caused the data to be saved run_command=/userdata view %2% %3%) [⏏ %9%](color=#62a9f5-#7ab8fa show_text=&7Snapshot size:&7\n&8Estimated file size of the snapshot (in KiB) run_command=/userdata view %2% %3%)'
2525
data_list_item_invalid: '[%1%](dark_gray show_text=&7User Data Snapshot for %2%\n&8⚡ %4% suggest_command=/userdata delete %2% %3%) [%5%](dark_gray show_text=&7Pinned:\n&8Pinned snapshots won''t be automatically rotated. suggest_command=/userdata delete %2% %3%) [%6% ⚑ %8% ⏏ %9%](gray strikethrough show_text=&#ff3300&Invalid Data Snapshot\n&#ff7e5e&Click to delete\n\n&7⚠ %10% suggest_command=/userdata delete %2% %3%)'
26+
data_saved: '[Successfully saved a snapshot of %1%''s current user data.](#00fb9a)'
2627
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%)'
2728
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%)'
2829
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%)'

common/src/main/resources/locales/es-es.yml

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ locales:
2323
data_list_title: '[%1%''s user data snapshots:](#00fb9a) [(%2%-%3% of](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n'
2424
data_list_item: '[%1%](gray show_text=&7User Data Snapshot for %2%\n&8⚡ %4% run_command=/userdata view %2% %3%) [%5%](#d8ff2b show_text=&7Pinned:\n&8Pinned snapshots won''t be automatically rotated. run_command=/userdata view %2% %3%) [%6%](color=#ffc43b-#f5c962 show_text=&7Version timestamp:&7\n&8When the data was saved\n&8%7% run_command=/userdata view %2% %3%) [⚑ %8%](#23a825-#36f539 show_text=&7Save cause:\n&8What caused the data to be saved run_command=/userdata view %2% %3%) [⏏ %9%](color=#62a9f5-#7ab8fa show_text=&7Snapshot size:&7\n&8Estimated file size of the snapshot (in KiB) run_command=/userdata view %2% %3%)'
2525
data_list_item_invalid: '[%1%](dark_gray show_text=&7User Data Snapshot for %2%\n&8⚡ %4% suggest_command=/userdata delete %2% %3%) [%5%](dark_gray show_text=&7Pinned:\n&8Pinned snapshots won''t be automatically rotated. suggest_command=/userdata delete %2% %3%) [%6% ⚑ %8% ⏏ %9%](gray strikethrough show_text=&#ff3300&Invalid Data Snapshot\n&#ff7e5e&Click to delete\n\n&7⚠ %10% suggest_command=/userdata delete %2% %3%)'
26+
data_saved: '[Successfully saved a snapshot of %1%''s current user data.](#00fb9a)'
2627
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%)'
2728
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%)'
2829
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%)'

common/src/main/resources/locales/fr-fr.yml

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ locales:
2323
data_list_title: '[Les instantanés des données utilisateur de %1%:](#00fb9a) [(%2%-%3% sur](#00fb9a)[%4%](#00fb9a bold)[)](#00fb9a)\n'
2424
data_list_item: '[%1%](gray show_text=&7Instantané des données utilisateur pour %2%\n&8⚡ %4% run_command=/userdataview %2% %3%) [%5%](#d8ff2b show_text=&7Épinglé:\n&8Les instantanés épinglés ne serontpas automatiquement supprimés. run_command=/userdata view %2% %3%) [%6%](color=#ffc43b-#f5c962show_text=&7Horodatage de la version:&7\n&8Quand les données ont été enregistrées\n&8%7% run_command=/userdataview %2% %3%) [⚑ %8%](#23a825-#36f539 show_text=&7Cause de la sauvegarde:\n&8Ce qui a causél''enregistrement des données run_command=/userdata view %2% %3%) [⏏ %9%](color=#62a9f5-#7ab8fashow_text=&7Taille de l''instantané:&7\n&8Taille du fichier estimée de l''instantané (en KiB) run_command=/userdataview %2% %3%)'
2525
data_list_item_invalid: '[%1%](dark_gray show_text=&7Instantané des données utilisateur pour %2%\n&8⚡%4% suggest_command=/userdata delete %2% %3%) [%5%](dark_gray show_text=&7Épinglé:\n&8Lesinstantanés épinglés ne seront pas automatiquement supprimés. suggest_command=/userdata delete %2%%3%) [%6% ⚑ %8% ⏏ %9%](gray strikethrough show_text=&#ff3300&Instantané des donnéesinvalide\n&#ff7e5e&Cliquez pour supprimer\n\n&7⚠ %10% suggest_command=/userdata delete%2% %3%)'
26+
data_saved: '[Successfully saved a snapshot of %1%''s current user data.](#00fb9a)'
2627
data_deleted: '[❌ Instantané des données utilisateur supprimé avec succès](#00fb9a) [%1%](#00fb9ashow_text=&7UUID de la version:\n&8%2%) [pour](#00fb9a) [%3%.](#00fb9a show_text=&7UUID du joueur:\n&8%4%)'
2728
data_restored: '[⏪ Données utilisateur actuelles de %1% restaurées avec succès à partir de l''instantané](#00fb9a) [%3%.](#00fb9a show_text=&7UUID de la version:\n&8%4%)'
2829
data_pinned: '[※ Instantané des données utilisateur épinglé avec succès](#00fb9a) [%1%](#00fb9ashow_text=&7UUID de la version:\n&8%2%) [pour](#00fb9a) [%3%.](#00fb9a show_text=&7UUID du joueur:\n&8%4%)'

common/src/main/resources/locales/id-id.yml

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ locales:
2323
data_list_title: '[Cuplikan data %1%:](#00fb9a) [(%2%-%3% dari](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n'
2424
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%)'
2525
data_list_item_invalid: '[%1%](dark_gray show_text=&7User Data Snapshot for %2%\n&8⚡ %4% suggest_command=/userdata delete %2% %3%) [%5%](dark_gray show_text=&7Pinned:\n&8Pinned snapshots won''t be automatically rotated. suggest_command=/userdata delete %2% %3%) [%6% ⚑ %8% ⏏ %9%](gray strikethrough show_text=&#ff3300&Invalid Data Snapshot\n&#ff7e5e&Click to delete\n\n&7⚠ %10% suggest_command=/userdata delete %2% %3%)'
26+
data_saved: '[Successfully saved a snapshot of %1%''s current user data.](#00fb9a)'
2627
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%)'
2728
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%)'
2829
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%)'

0 commit comments

Comments
 (0)