mirror of
https://github.com/WiIIiam278/HuskSync.git
synced 2026-01-04 15:31:37 +00:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
39767c5cd0 | ||
|
|
48f7037898 | ||
|
|
67dddf0cfa | ||
|
|
eeb5e57c1e | ||
|
|
5a6ea2cffe | ||
|
|
07ddd34f8e | ||
|
|
a0b86c298f | ||
|
|
6fbef032bc | ||
|
|
318aacd432 | ||
|
|
ba1b2ff62e |
@@ -3,7 +3,7 @@ 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
|
||||||
id 'org.ajoberstar.grgit' version '5.2.1'
|
id 'org.ajoberstar.grgit' version '5.2.2'
|
||||||
id 'maven-publish'
|
id 'maven-publish'
|
||||||
id 'java'
|
id 'java'
|
||||||
}
|
}
|
||||||
@@ -20,6 +20,7 @@ ext {
|
|||||||
set 'jedis_version', jedis_version.toString()
|
set 'jedis_version', jedis_version.toString()
|
||||||
set 'mysql_driver_version', mysql_driver_version.toString()
|
set 'mysql_driver_version', mysql_driver_version.toString()
|
||||||
set 'mariadb_driver_version', mariadb_driver_version.toString()
|
set 'mariadb_driver_version', mariadb_driver_version.toString()
|
||||||
|
set 'mongodb_driver_version', mongodb_driver_version.toString()
|
||||||
set 'snappy_version', snappy_version.toString()
|
set 'snappy_version', snappy_version.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ dependencies {
|
|||||||
compileOnly 'commons-io:commons-io:2.15.1'
|
compileOnly 'commons-io:commons-io:2.15.1'
|
||||||
compileOnly 'org.json:json:20240205'
|
compileOnly 'org.json:json:20240205'
|
||||||
compileOnly 'de.themoep:minedown-adventure:1.7.2-SNAPSHOT'
|
compileOnly 'de.themoep:minedown-adventure:1.7.2-SNAPSHOT'
|
||||||
compileOnly 'com.github.Exlll.ConfigLib:configlib-yaml:v4.4.0'
|
compileOnly 'com.github.Exlll.ConfigLib:configlib-yaml:v4.5.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:AdvancementAPI:97a9583413'
|
compileOnly 'net.william278:AdvancementAPI:97a9583413'
|
||||||
@@ -39,7 +39,7 @@ shadowJar {
|
|||||||
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 'de.exlll', 'net.william278.huskclaims.libraries'
|
relocate 'de.exlll', 'net.william278.husksync.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'
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ import net.william278.husksync.data.Data;
|
|||||||
import net.william278.husksync.data.Identifier;
|
import net.william278.husksync.data.Identifier;
|
||||||
import net.william278.husksync.data.Serializer;
|
import net.william278.husksync.data.Serializer;
|
||||||
import net.william278.husksync.database.Database;
|
import net.william278.husksync.database.Database;
|
||||||
|
import net.william278.husksync.database.MongoDbDatabase;
|
||||||
import net.william278.husksync.database.MySqlDatabase;
|
import net.william278.husksync.database.MySqlDatabase;
|
||||||
import net.william278.husksync.event.BukkitEventDispatcher;
|
import net.william278.husksync.event.BukkitEventDispatcher;
|
||||||
import net.william278.husksync.hook.PlanHook;
|
import net.william278.husksync.hook.PlanHook;
|
||||||
@@ -162,7 +163,11 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync, BukkitTask.S
|
|||||||
|
|
||||||
// Initialize the database
|
// Initialize the database
|
||||||
initialize(getSettings().getDatabase().getType().getDisplayName() + " database connection", (plugin) -> {
|
initialize(getSettings().getDatabase().getType().getDisplayName() + " database connection", (plugin) -> {
|
||||||
this.database = new MySqlDatabase(this);
|
this.database = switch (settings.getDatabase().getType()) {
|
||||||
|
case MYSQL, MARIADB -> new MySqlDatabase(this);
|
||||||
|
case MONGO -> new MongoDbDatabase(this);
|
||||||
|
default -> throw new IllegalStateException("Invalid database type");
|
||||||
|
};
|
||||||
this.database.initialize();
|
this.database.initialize();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import net.william278.husksync.user.OnlineUser;
|
|||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.entity.Projectile;
|
import org.bukkit.entity.Projectile;
|
||||||
|
import org.bukkit.event.Cancellable;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.EventPriority;
|
import org.bukkit.event.EventPriority;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
@@ -49,6 +50,7 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class BukkitEventListener extends EventListener implements BukkitJoinEventListener, BukkitQuitEventListener,
|
public class BukkitEventListener extends EventListener implements BukkitJoinEventListener, BukkitQuitEventListener,
|
||||||
@@ -132,52 +134,52 @@ public class BukkitEventListener extends EventListener implements BukkitJoinEven
|
|||||||
public void onProjectileLaunch(@NotNull ProjectileLaunchEvent event) {
|
public void onProjectileLaunch(@NotNull ProjectileLaunchEvent event) {
|
||||||
final Projectile projectile = event.getEntity();
|
final Projectile projectile = event.getEntity();
|
||||||
if (projectile.getShooter() instanceof Player player) {
|
if (projectile.getShooter() instanceof Player player) {
|
||||||
event.setCancelled(cancelPlayerEvent(player.getUniqueId()));
|
cancelPlayerEvent(player.getUniqueId(), event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||||
public void onDropItem(@NotNull PlayerDropItemEvent event) {
|
public void onDropItem(@NotNull PlayerDropItemEvent event) {
|
||||||
event.setCancelled(cancelPlayerEvent(event.getPlayer().getUniqueId()));
|
cancelPlayerEvent(event.getPlayer().getUniqueId(), event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||||
public void onPickupItem(@NotNull EntityPickupItemEvent event) {
|
public void onPickupItem(@NotNull EntityPickupItemEvent event) {
|
||||||
if (event.getEntity() instanceof Player player) {
|
if (event.getEntity() instanceof Player player) {
|
||||||
event.setCancelled(cancelPlayerEvent(player.getUniqueId()));
|
cancelPlayerEvent(player.getUniqueId(), event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||||
public void onPlayerInteract(@NotNull PlayerInteractEvent event) {
|
public void onPlayerInteract(@NotNull PlayerInteractEvent event) {
|
||||||
event.setCancelled(cancelPlayerEvent(event.getPlayer().getUniqueId()));
|
cancelPlayerEvent(event.getPlayer().getUniqueId(), event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||||
public void onPlayerInteractEntity(@NotNull PlayerInteractEntityEvent event) {
|
public void onPlayerInteractEntity(@NotNull PlayerInteractEntityEvent event) {
|
||||||
event.setCancelled(cancelPlayerEvent(event.getPlayer().getUniqueId()));
|
cancelPlayerEvent(event.getPlayer().getUniqueId(), event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||||
public void onBlockPlace(@NotNull BlockPlaceEvent event) {
|
public void onBlockPlace(@NotNull BlockPlaceEvent event) {
|
||||||
event.setCancelled(cancelPlayerEvent(event.getPlayer().getUniqueId()));
|
cancelPlayerEvent(event.getPlayer().getUniqueId(), event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||||
public void onBlockBreak(@NotNull BlockBreakEvent event) {
|
public void onBlockBreak(@NotNull BlockBreakEvent event) {
|
||||||
event.setCancelled(cancelPlayerEvent(event.getPlayer().getUniqueId()));
|
cancelPlayerEvent(event.getPlayer().getUniqueId(), event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||||
public void onInventoryOpen(@NotNull InventoryOpenEvent event) {
|
public void onInventoryOpen(@NotNull InventoryOpenEvent event) {
|
||||||
if (event.getPlayer() instanceof Player player) {
|
if (event.getPlayer() instanceof Player player) {
|
||||||
event.setCancelled(cancelPlayerEvent(player.getUniqueId()));
|
cancelPlayerEvent(player.getUniqueId(), event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||||
public void onInventoryClick(@NotNull InventoryClickEvent event) {
|
public void onInventoryClick(@NotNull InventoryClickEvent event) {
|
||||||
event.setCancelled(cancelPlayerEvent(event.getWhoClicked().getUniqueId()));
|
cancelPlayerEvent(event.getWhoClicked().getUniqueId(), event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||||
@@ -187,7 +189,7 @@ public class BukkitEventListener extends EventListener implements BukkitJoinEven
|
|||||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||||
public void onPlayerTakeDamage(@NotNull EntityDamageEvent event) {
|
public void onPlayerTakeDamage(@NotNull EntityDamageEvent event) {
|
||||||
if (event.getEntity() instanceof Player player) {
|
if (event.getEntity() instanceof Player player) {
|
||||||
event.setCancelled(cancelPlayerEvent(player.getUniqueId()));
|
cancelPlayerEvent(player.getUniqueId(), event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,7 +199,13 @@ public class BukkitEventListener extends EventListener implements BukkitJoinEven
|
|||||||
final String commandLabel = commandArgs[0].toLowerCase(Locale.ENGLISH);
|
final String commandLabel = commandArgs[0].toLowerCase(Locale.ENGLISH);
|
||||||
|
|
||||||
if (blacklistedCommands.contains("*") || blacklistedCommands.contains(commandLabel)) {
|
if (blacklistedCommands.contains("*") || blacklistedCommands.contains(commandLabel)) {
|
||||||
event.setCancelled(cancelPlayerEvent(event.getPlayer().getUniqueId()));
|
cancelPlayerEvent(event.getPlayer().getUniqueId(), event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cancelPlayerEvent(@NotNull UUID uuid, @NotNull Cancellable event) {
|
||||||
|
if (cancelPlayerEvent(uuid)) {
|
||||||
|
event.setCancelled(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,4 +12,5 @@ libraries:
|
|||||||
- 'redis.clients:jedis:${jedis_version}'
|
- 'redis.clients:jedis:${jedis_version}'
|
||||||
- 'com.mysql:mysql-connector-j:${mysql_driver_version}'
|
- 'com.mysql:mysql-connector-j:${mysql_driver_version}'
|
||||||
- 'org.mariadb.jdbc:mariadb-java-client:${mariadb_driver_version}'
|
- 'org.mariadb.jdbc:mariadb-java-client:${mariadb_driver_version}'
|
||||||
|
- 'org.mongodb:mongodb-driver:${mongodb_driver_version}'
|
||||||
- 'org.xerial.snappy:snappy-java:${snappy_version}'
|
- 'org.xerial.snappy:snappy-java:${snappy_version}'
|
||||||
@@ -9,7 +9,7 @@ dependencies {
|
|||||||
api 'org.json:json:20240205'
|
api 'org.json:json:20240205'
|
||||||
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 'com.github.Exlll.ConfigLib:configlib-yaml:v4.4.0'
|
api 'com.github.Exlll.ConfigLib:configlib-yaml:v4.5.0'
|
||||||
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') {
|
||||||
@@ -18,20 +18,21 @@ dependencies {
|
|||||||
|
|
||||||
compileOnly 'org.projectlombok:lombok:1.18.30'
|
compileOnly 'org.projectlombok:lombok:1.18.30'
|
||||||
compileOnly 'org.jetbrains:annotations:24.1.0'
|
compileOnly 'org.jetbrains:annotations:24.1.0'
|
||||||
compileOnly 'net.kyori:adventure-api:4.15.0'
|
compileOnly 'net.kyori:adventure-api:4.16.0'
|
||||||
compileOnly 'net.kyori:adventure-platform-api:4.3.2'
|
compileOnly 'net.kyori:adventure-platform-api:4.3.2'
|
||||||
compileOnly 'com.google.guava:guava:33.0.0-jre'
|
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.mongodb:mongodb-driver:$mongodb_driver_version"
|
||||||
compileOnly "org.xerial.snappy:snappy-java:$snappy_version"
|
compileOnly "org.xerial.snappy:snappy-java:$snappy_version"
|
||||||
|
|
||||||
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"
|
||||||
testImplementation 'com.google.guava:guava:33.0.0-jre'
|
testImplementation 'com.google.guava:guava:33.0.0-jre'
|
||||||
testImplementation 'com.github.plan-player-analytics:Plan:5.5.2272'
|
testImplementation 'com.github.plan-player-analytics:Plan:5.5.2272'
|
||||||
testCompileOnly 'com.github.Exlll.ConfigLib:configlib-yaml:v4.4.0'
|
testCompileOnly 'com.github.Exlll.ConfigLib:configlib-yaml:v4.5.0'
|
||||||
testCompileOnly 'org.jetbrains:annotations:24.1.0'
|
testCompileOnly 'org.jetbrains:annotations:24.1.0'
|
||||||
|
|
||||||
annotationProcessor 'org.projectlombok:lombok:1.18.30'
|
annotationProcessor 'org.projectlombok:lombok:1.18.30'
|
||||||
|
|||||||
@@ -379,7 +379,7 @@ public interface HuskSync extends Task.Supplier, EventDispatcher, ConfigProvider
|
|||||||
HuskSync has failed to load! The plugin will not be enabled and no data will be synchronized.
|
HuskSync has failed to load! The plugin will not be enabled and no data will be synchronized.
|
||||||
Please make sure the plugin has been setup correctly (https://william278.net/docs/husksync/setup):
|
Please make sure the plugin has been setup correctly (https://william278.net/docs/husksync/setup):
|
||||||
|
|
||||||
1) Make sure you've entered your MySQL or MariaDB database details correctly in config.yml
|
1) Make sure you've entered your MySQL, MariaDB or MongoDB 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-file)
|
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
|
||||||
|
|||||||
@@ -66,7 +66,8 @@ public class HuskSyncCommand extends Command implements TabProvider {
|
|||||||
AboutMenu.Credit.of("William278").description("Click to visit website").url("https://william278.net"))
|
AboutMenu.Credit.of("William278").description("Click to visit website").url("https://william278.net"))
|
||||||
.credits("Contributors",
|
.credits("Contributors",
|
||||||
AboutMenu.Credit.of("HarvelsX").description("Code"),
|
AboutMenu.Credit.of("HarvelsX").description("Code"),
|
||||||
AboutMenu.Credit.of("HookWoods").description("Code"))
|
AboutMenu.Credit.of("HookWoods").description("Code"),
|
||||||
|
AboutMenu.Credit.of("Preva1l").description("Code"))
|
||||||
.credits("Translators",
|
.credits("Translators",
|
||||||
AboutMenu.Credit.of("Namiu").description("Japanese (ja-jp)"),
|
AboutMenu.Credit.of("Namiu").description("Japanese (ja-jp)"),
|
||||||
AboutMenu.Credit.of("anchelthe").description("Spanish (es-es)"),
|
AboutMenu.Credit.of("anchelthe").description("Spanish (es-es)"),
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import net.william278.husksync.data.Identifier;
|
|||||||
import net.william278.husksync.database.Database;
|
import net.william278.husksync.database.Database;
|
||||||
import net.william278.husksync.listener.EventListener;
|
import net.william278.husksync.listener.EventListener;
|
||||||
import net.william278.husksync.sync.DataSyncer;
|
import net.william278.husksync.sync.DataSyncer;
|
||||||
|
import org.checkerframework.checker.units.qual.C;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -85,10 +86,10 @@ public class Settings {
|
|||||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
public static class DatabaseSettings {
|
public static class DatabaseSettings {
|
||||||
|
|
||||||
@Comment("Type of database to use (MYSQL, MARIADB)")
|
@Comment("Type of database to use (MYSQL, MARIADB, MONGO)")
|
||||||
private Database.Type type = Database.Type.MYSQL;
|
private Database.Type type = Database.Type.MYSQL;
|
||||||
|
|
||||||
@Comment("Specify credentials here for your MYSQL or MARIADB database")
|
@Comment("Specify credentials here for your MYSQL, MARIADB OR MONGO database")
|
||||||
private DatabaseCredentials credentials = new DatabaseCredentials();
|
private DatabaseCredentials credentials = new DatabaseCredentials();
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@@ -100,9 +101,12 @@ public class Settings {
|
|||||||
private String database = "HuskSync";
|
private String database = "HuskSync";
|
||||||
private String username = "root";
|
private String username = "root";
|
||||||
private String password = "pa55w0rd";
|
private String password = "pa55w0rd";
|
||||||
|
@Comment("Only change this if you have select MYSQL or MARIADB")
|
||||||
private String parameters = String.join("&",
|
private String parameters = String.join("&",
|
||||||
"?autoReconnect=true", "useSSL=false",
|
"?autoReconnect=true", "useSSL=false",
|
||||||
"useUnicode=true", "characterEncoding=UTF-8");
|
"useUnicode=true", "characterEncoding=UTF-8");
|
||||||
|
@Comment("Only change this if you have selected MONGO")
|
||||||
|
private String mongoAuthDb = "admin";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Comment("MYSQL / MARIADB database Hikari connection pool properties. Don't modify this unless you know what you're doing!")
|
@Comment("MYSQL / MARIADB database Hikari connection pool properties. Don't modify this unless you know what you're doing!")
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
package net.william278.husksync.database;
|
package net.william278.husksync.database;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Getter;
|
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;
|
||||||
@@ -253,9 +254,11 @@ public abstract class Database {
|
|||||||
/**
|
/**
|
||||||
* Identifies types of databases
|
* Identifies types of databases
|
||||||
*/
|
*/
|
||||||
|
@Getter
|
||||||
public enum Type {
|
public enum Type {
|
||||||
MYSQL("MySQL", "mysql"),
|
MYSQL("MySQL", "mysql"),
|
||||||
MARIADB("MariaDB", "mariadb");
|
MARIADB("MariaDB", "mariadb"),
|
||||||
|
MONGO("MongoDB", "mongo");
|
||||||
|
|
||||||
private final String displayName;
|
private final String displayName;
|
||||||
private final String protocol;
|
private final String protocol;
|
||||||
@@ -264,16 +267,6 @@ public abstract class Database {
|
|||||||
this.displayName = displayName;
|
this.displayName = displayName;
|
||||||
this.protocol = protocol;
|
this.protocol = protocol;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public String getDisplayName() {
|
|
||||||
return displayName;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public String getProtocol() {
|
|
||||||
return protocol;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,382 @@
|
|||||||
|
/*
|
||||||
|
* 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.database;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.mongodb.MongoException;
|
||||||
|
import com.mongodb.client.FindIterable;
|
||||||
|
import com.mongodb.client.model.Updates;
|
||||||
|
import net.william278.husksync.HuskSync;
|
||||||
|
import net.william278.husksync.config.Settings;
|
||||||
|
import net.william278.husksync.data.DataSnapshot;
|
||||||
|
import net.william278.husksync.database.mongo.MongoCollectionHelper;
|
||||||
|
import net.william278.husksync.database.mongo.MongoConnectionHandler;
|
||||||
|
import net.william278.husksync.user.User;
|
||||||
|
import org.bson.Document;
|
||||||
|
import org.bson.conversions.Bson;
|
||||||
|
import org.bson.types.Binary;
|
||||||
|
import org.jetbrains.annotations.Blocking;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
public class MongoDbDatabase extends Database {
|
||||||
|
private MongoConnectionHandler mongoConnectionHandler;
|
||||||
|
private MongoCollectionHelper mongoCollectionHelper;
|
||||||
|
|
||||||
|
private final String usersTable;
|
||||||
|
private final String userDataTable;
|
||||||
|
public MongoDbDatabase(@NotNull HuskSync plugin) {
|
||||||
|
super(plugin);
|
||||||
|
this.usersTable = plugin.getSettings().getDatabase().getTableName(TableName.USERS);
|
||||||
|
this.userDataTable = plugin.getSettings().getDatabase().getTableName(TableName.USER_DATA);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the database and ensure tables are present; create tables if they do not exist.
|
||||||
|
*
|
||||||
|
* @throws IllegalStateException if the database could not be initialized
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void initialize() throws IllegalStateException {
|
||||||
|
final Settings.DatabaseSettings.DatabaseCredentials credentials = plugin.getSettings().getDatabase().getCredentials();
|
||||||
|
try {
|
||||||
|
mongoConnectionHandler = new MongoConnectionHandler(
|
||||||
|
credentials.getHost(),
|
||||||
|
credentials.getPort(),
|
||||||
|
credentials.getUsername(),
|
||||||
|
credentials.getPassword(),
|
||||||
|
credentials.getDatabase(),
|
||||||
|
credentials.getMongoAuthDb()
|
||||||
|
);
|
||||||
|
mongoCollectionHelper = new MongoCollectionHelper(mongoConnectionHandler);
|
||||||
|
if (mongoCollectionHelper.getCollection(usersTable) == null) {
|
||||||
|
mongoCollectionHelper.createCollection(usersTable);
|
||||||
|
}
|
||||||
|
if (mongoCollectionHelper.getCollection(userDataTable) == null) {
|
||||||
|
mongoCollectionHelper.createCollection(userDataTable);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IllegalStateException("Failed to establish a connection to the MongoDB database. " +
|
||||||
|
"Please check the supplied database credentials in the config file", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure a {@link User} has an entry in the database and that their username is up-to-date
|
||||||
|
*
|
||||||
|
* @param user The {@link User} to ensure
|
||||||
|
*/
|
||||||
|
@Blocking
|
||||||
|
@Override
|
||||||
|
public void ensureUser(@NotNull User user) {
|
||||||
|
getUser(user.getUuid()).ifPresentOrElse(
|
||||||
|
existingUser -> {
|
||||||
|
if (!existingUser.getUsername().equals(user.getUsername())) {
|
||||||
|
// Update a user's name if it has changed in the database
|
||||||
|
try {
|
||||||
|
Document filter = new Document("uuid", existingUser.getUuid().toString());
|
||||||
|
Document doc = mongoCollectionHelper.getCollection(usersTable).find(filter).first();
|
||||||
|
|
||||||
|
Bson updates = Updates.set("uuid", user.getUuid().toString());
|
||||||
|
mongoCollectionHelper.updateDocument(usersTable, doc, updates);
|
||||||
|
} catch (MongoException e) {
|
||||||
|
plugin.log(Level.SEVERE, "Failed to insert a user into the database", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
() -> {
|
||||||
|
// Insert new player data into the database
|
||||||
|
try {
|
||||||
|
Document doc = new Document("uuid", user.getUuid().toString()).append("username", user.getUsername());
|
||||||
|
mongoCollectionHelper.insertDocument(usersTable, doc);
|
||||||
|
} catch (MongoException e) {
|
||||||
|
plugin.log(Level.SEVERE, "Failed to insert a user into the database", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a player by their Minecraft account {@link UUID}
|
||||||
|
*
|
||||||
|
* @param uuid Minecraft account {@link UUID} of the {@link User} to get
|
||||||
|
* @return An optional with the {@link User} present if they exist
|
||||||
|
*/
|
||||||
|
@Blocking
|
||||||
|
@Override
|
||||||
|
public Optional<User> getUser(@NotNull UUID uuid) {
|
||||||
|
Document filter = new Document("uuid", uuid);
|
||||||
|
Document doc = mongoCollectionHelper.getCollection(usersTable).find(filter).first();
|
||||||
|
if (doc != null) {
|
||||||
|
return Optional.of(new User(UUID.fromString(doc.getString("uuid")),
|
||||||
|
doc.getString("username")));
|
||||||
|
}
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a user by their username (<i>case-insensitive</i>)
|
||||||
|
*
|
||||||
|
* @param username Username of the {@link User} to get (<i>case-insensitive</i>)
|
||||||
|
* @return An optional with the {@link User} present if they exist
|
||||||
|
*/
|
||||||
|
@Blocking
|
||||||
|
@Override
|
||||||
|
public Optional<User> getUserByName(@NotNull String username) {
|
||||||
|
Document filter = new Document("username", username);
|
||||||
|
Document doc = mongoCollectionHelper.getCollection(usersTable).find(filter).first();
|
||||||
|
if (doc != null) {
|
||||||
|
return Optional.of(new User(UUID.fromString(doc.getString("uuid")),
|
||||||
|
doc.getString("username")));
|
||||||
|
}
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the latest data snapshot for a user.
|
||||||
|
*
|
||||||
|
* @param user The user to get data for
|
||||||
|
* @return an optional containing the {@link DataSnapshot}, if it exists, or an empty optional if it does not
|
||||||
|
*/
|
||||||
|
@Blocking
|
||||||
|
@Override
|
||||||
|
public Optional<DataSnapshot.Packed> getLatestSnapshot(@NotNull User user) {
|
||||||
|
Document filter = new Document("player_uuid", user.getUuid().toString());
|
||||||
|
Document sort = new Document("timestamp", -1); // -1 = Descending
|
||||||
|
FindIterable<Document> iterable = mongoCollectionHelper.getCollection(userDataTable).find(filter).sort(sort);
|
||||||
|
Document doc = iterable.first();
|
||||||
|
if (doc != null) {
|
||||||
|
final UUID versionUuid = UUID.fromString(doc.getString("version_uuid"));
|
||||||
|
final OffsetDateTime timestamp = OffsetDateTime.ofInstant(Instant.ofEpochMilli((long) doc.get("timestamp")), TimeZone.getDefault().toZoneId());
|
||||||
|
final Binary bin = doc.get("data", Binary.class);
|
||||||
|
final byte[] dataByteArray = bin.getData();
|
||||||
|
return Optional.of(DataSnapshot.deserialize(plugin, dataByteArray, versionUuid, timestamp));
|
||||||
|
}
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all {@link DataSnapshot} entries for a user from the database.
|
||||||
|
*
|
||||||
|
* @param user The user to get data for
|
||||||
|
* @return The list of a user's {@link DataSnapshot} entries
|
||||||
|
*/
|
||||||
|
@Blocking
|
||||||
|
@Override
|
||||||
|
@NotNull
|
||||||
|
public List<DataSnapshot.Packed> getAllSnapshots(@NotNull User user) {
|
||||||
|
final List<DataSnapshot.Packed> retrievedData = Lists.newArrayList();
|
||||||
|
Document filter = new Document("player_uuid", user.getUuid().toString());
|
||||||
|
Document sort = new Document("timestamp", -1); // -1 = Descending
|
||||||
|
FindIterable<Document> iterable = mongoCollectionHelper.getCollection(userDataTable).find(filter).sort(sort);
|
||||||
|
for (Document doc : iterable) {
|
||||||
|
final UUID versionUuid = UUID.fromString(doc.getString("version_uuid"));
|
||||||
|
final OffsetDateTime timestamp = OffsetDateTime.ofInstant(Instant.ofEpochMilli((long) doc.get("timestamp")), TimeZone.getDefault().toZoneId());
|
||||||
|
final Binary bin = doc.get("data", Binary.class);
|
||||||
|
final byte[] dataByteArray = bin.getData();
|
||||||
|
retrievedData.add(DataSnapshot.deserialize(plugin, dataByteArray, versionUuid, timestamp));
|
||||||
|
}
|
||||||
|
return retrievedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a specific {@link DataSnapshot} entry for a user from the database, by its UUID.
|
||||||
|
*
|
||||||
|
* @param user The user to get data for
|
||||||
|
* @param versionUuid The UUID of the {@link DataSnapshot} entry to get
|
||||||
|
* @return An optional containing the {@link DataSnapshot}, if it exists
|
||||||
|
*/
|
||||||
|
@Blocking
|
||||||
|
@Override
|
||||||
|
public Optional<DataSnapshot.Packed> getSnapshot(@NotNull User user, @NotNull UUID versionUuid) {
|
||||||
|
Document filter = new Document("player_uuid", user.getUuid().toString()).append("version_uuid", versionUuid.toString());
|
||||||
|
Document sort = new Document("timestamp", -1); // -1 = Descending
|
||||||
|
FindIterable<Document> iterable = mongoCollectionHelper.getCollection(userDataTable).find(filter).sort(sort);
|
||||||
|
Document doc = iterable.first();
|
||||||
|
if (doc != null) {
|
||||||
|
final OffsetDateTime timestamp = OffsetDateTime.ofInstant(Instant.ofEpochMilli((long) doc.get("timestamp")), TimeZone.getDefault().toZoneId());
|
||||||
|
final Binary bin = doc.get("data", Binary.class);
|
||||||
|
final byte[] dataByteArray = bin.getData();
|
||||||
|
return Optional.of(DataSnapshot.deserialize(plugin, dataByteArray, versionUuid, timestamp));
|
||||||
|
}
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <b>(Internal)</b> Prune user data for a given user to the maximum value as configured.
|
||||||
|
*
|
||||||
|
* @param user The user to prune data for
|
||||||
|
* @implNote Data snapshots marked as {@code pinned} are exempt from rotation
|
||||||
|
*/
|
||||||
|
@Blocking
|
||||||
|
@Override
|
||||||
|
protected void rotateSnapshots(@NotNull User user) {
|
||||||
|
try {
|
||||||
|
final List<DataSnapshot.Packed> unpinnedUserData = getAllSnapshots(user).stream()
|
||||||
|
.filter(dataSnapshot -> !dataSnapshot.isPinned()).toList();
|
||||||
|
final int maxSnapshots = plugin.getSettings().getSynchronization().getMaxUserDataSnapshots();
|
||||||
|
if (unpinnedUserData.size() > maxSnapshots) {
|
||||||
|
|
||||||
|
Document filter = new Document("player_uuid", user.getUuid().toString()).append("pinned", false);
|
||||||
|
Document sort = new Document("timestamp", 1); // 1 = Ascending
|
||||||
|
FindIterable<Document> iterable = mongoCollectionHelper.getCollection(userDataTable)
|
||||||
|
.find(filter)
|
||||||
|
.sort(sort)
|
||||||
|
.limit(unpinnedUserData.size() - maxSnapshots);
|
||||||
|
|
||||||
|
for (Document doc : iterable) {
|
||||||
|
mongoCollectionHelper.deleteDocument(userDataTable, doc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (MongoException e) {
|
||||||
|
plugin.log(Level.SEVERE, "Failed to prune user data from the database", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a specific {@link DataSnapshot} entry for a user from the database, by its UUID.
|
||||||
|
*
|
||||||
|
* @param user The user to get data for
|
||||||
|
* @param versionUuid The UUID of the {@link DataSnapshot} entry to delete
|
||||||
|
*/
|
||||||
|
@Blocking
|
||||||
|
@Override
|
||||||
|
public boolean deleteSnapshot(@NotNull User user, @NotNull UUID versionUuid) {
|
||||||
|
try {
|
||||||
|
Document filter = new Document("player_uuid", user.getUuid().toString()).append("version_uuid", versionUuid.toString());
|
||||||
|
Document doc = mongoCollectionHelper.getCollection(userDataTable).find(filter).first();
|
||||||
|
if (doc == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
mongoCollectionHelper.deleteDocument(userDataTable, doc);
|
||||||
|
return true;
|
||||||
|
} catch (MongoException e) {
|
||||||
|
plugin.log(Level.SEVERE, "Failed to delete specific user data from the database", e);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the most recent data snapshot by the given {@link User user}
|
||||||
|
* The snapshot must have been created after {@link OffsetDateTime time} and NOT be pinned
|
||||||
|
* Facilities the backup frequency feature, reducing redundant snapshots from being saved longer than needed
|
||||||
|
*
|
||||||
|
* @param user The user to delete a snapshot for
|
||||||
|
* @param within The time to delete a snapshot after
|
||||||
|
*/
|
||||||
|
@Blocking
|
||||||
|
@Override
|
||||||
|
protected void rotateLatestSnapshot(@NotNull User user, @NotNull OffsetDateTime within) {
|
||||||
|
try {
|
||||||
|
Document filter = new Document("player_uuid", user.getUuid().toString()).append("pinned", false);
|
||||||
|
Document sort = new Document("timestamp", 1); // 1 = Ascending
|
||||||
|
FindIterable<Document> iterable = mongoCollectionHelper.getCollection(userDataTable)
|
||||||
|
.find(filter)
|
||||||
|
.sort(sort);
|
||||||
|
|
||||||
|
for (Document doc : iterable) {
|
||||||
|
final OffsetDateTime timestamp = OffsetDateTime.ofInstant(
|
||||||
|
Instant.ofEpochMilli((long) doc.get("timestamp")), TimeZone.getDefault().toZoneId()
|
||||||
|
);
|
||||||
|
if (timestamp.isAfter(within)) {
|
||||||
|
mongoCollectionHelper.deleteDocument(userDataTable, doc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (MongoException e) {
|
||||||
|
plugin.log(Level.SEVERE, "Failed to prune user data from the database", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <b>Internal</b> - Create user data in the database
|
||||||
|
*
|
||||||
|
* @param user The user to add data for
|
||||||
|
* @param data The {@link DataSnapshot} to set.
|
||||||
|
*/
|
||||||
|
@Blocking
|
||||||
|
@Override
|
||||||
|
protected void createSnapshot(@NotNull User user, @NotNull DataSnapshot.Packed data) {
|
||||||
|
try {
|
||||||
|
Document doc = new Document("player_uuid", user.getUuid().toString())
|
||||||
|
.append("version_uuid", data.getId().toString())
|
||||||
|
.append("timestamp", data.getTimestamp().toInstant().toEpochMilli())
|
||||||
|
.append("save_cause", data.getSaveCause().name())
|
||||||
|
.append("pinned", data.isPinned())
|
||||||
|
.append("data", new Binary(data.asBytes(plugin)));
|
||||||
|
mongoCollectionHelper.insertDocument(userDataTable, doc);
|
||||||
|
} catch (MongoException e) {
|
||||||
|
plugin.log(Level.SEVERE, "Failed to set user data in the database", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update a saved {@link DataSnapshot} by given version UUID
|
||||||
|
*
|
||||||
|
* @param user The user whose data snapshot
|
||||||
|
* @param data The {@link DataSnapshot} to update
|
||||||
|
*/
|
||||||
|
@Blocking
|
||||||
|
@Override
|
||||||
|
public void updateSnapshot(@NotNull User user, @NotNull DataSnapshot.Packed data) {
|
||||||
|
try {
|
||||||
|
Document doc = new Document("player_uuid", user.getUuid().toString()).append("version_uuid", data.getId().toString());
|
||||||
|
Bson updates = Updates.combine(
|
||||||
|
Updates.set("save_cause", data.getSaveCause().name()),
|
||||||
|
Updates.set("pinned", data.isPinned()),
|
||||||
|
Updates.set("data", new Binary(data.asBytes(plugin)))
|
||||||
|
);
|
||||||
|
mongoCollectionHelper.updateDocument(userDataTable, doc, updates);
|
||||||
|
} catch (MongoException e) {
|
||||||
|
plugin.log(Level.SEVERE, "Failed to pin user data in the database", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wipes <b>all</b> {@link User} entries from the database.
|
||||||
|
* <b>This should only be used when preparing tables for a data migration.</b>
|
||||||
|
*/
|
||||||
|
@Blocking
|
||||||
|
@Override
|
||||||
|
public void wipeDatabase() {
|
||||||
|
try {
|
||||||
|
mongoCollectionHelper.deleteCollection(usersTable);
|
||||||
|
} catch (MongoException e) {
|
||||||
|
plugin.log(Level.SEVERE, "Failed to wipe the database", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the database connection
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void terminate() {
|
||||||
|
if (mongoConnectionHandler != null) {
|
||||||
|
mongoConnectionHandler.closeConnection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* 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.database.mongo;
|
||||||
|
|
||||||
|
import com.mongodb.client.MongoCollection;
|
||||||
|
import org.bson.Document;
|
||||||
|
import org.bson.conversions.Bson;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public class MongoCollectionHelper {
|
||||||
|
private final MongoConnectionHandler database;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the collection helper
|
||||||
|
* @param database Instance of {@link MongoConnectionHandler}
|
||||||
|
*/
|
||||||
|
public MongoCollectionHelper(@NotNull MongoConnectionHandler database) {
|
||||||
|
this.database = database;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a collection
|
||||||
|
* @param collectionName the collection name
|
||||||
|
*/
|
||||||
|
public void createCollection(@NotNull String collectionName) {
|
||||||
|
database.getDatabase().createCollection(collectionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a collection
|
||||||
|
* @param collectionName the collection name
|
||||||
|
*/
|
||||||
|
public void deleteCollection(@NotNull String collectionName) {
|
||||||
|
database.getDatabase().getCollection(collectionName).drop();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a collection
|
||||||
|
* @param collectionName the collection name
|
||||||
|
* @return MongoCollection<Document>
|
||||||
|
*/
|
||||||
|
public MongoCollection<Document> getCollection(@NotNull String collectionName) {
|
||||||
|
return database.getDatabase().getCollection(collectionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a document to a collection
|
||||||
|
* @param collectionName collection to add to
|
||||||
|
* @param document Document to add
|
||||||
|
*/
|
||||||
|
public void insertDocument(@NotNull String collectionName, @NotNull Document document) {
|
||||||
|
MongoCollection<Document> collection = database.getDatabase().getCollection(collectionName);
|
||||||
|
collection.insertOne(document);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update a document
|
||||||
|
* @param collectionName collection the document is in
|
||||||
|
* @param document filter of document
|
||||||
|
* @param updates Bson of updates
|
||||||
|
*/
|
||||||
|
public void updateDocument(@NotNull String collectionName, @NotNull Document document, @NotNull Bson updates) {
|
||||||
|
MongoCollection<Document> collection = database.getDatabase().getCollection(collectionName);
|
||||||
|
collection.updateOne(document, updates);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a document
|
||||||
|
* @param collectionName collection the document is in
|
||||||
|
* @param document filter to remove
|
||||||
|
*/
|
||||||
|
public void deleteDocument(@NotNull String collectionName, @NotNull Document document) {
|
||||||
|
MongoCollection<Document> collection = database.getDatabase().getCollection(collectionName);
|
||||||
|
collection.deleteOne(document);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* 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.database.mongo;
|
||||||
|
|
||||||
|
import com.mongodb.MongoClientSettings;
|
||||||
|
import com.mongodb.MongoCredential;
|
||||||
|
import com.mongodb.ServerAddress;
|
||||||
|
import com.mongodb.client.MongoClient;
|
||||||
|
import com.mongodb.client.MongoClients;
|
||||||
|
import com.mongodb.client.MongoDatabase;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public class MongoConnectionHandler {
|
||||||
|
private final MongoClient mongoClient;
|
||||||
|
private final MongoDatabase database;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiate a connection to a Mongo Server
|
||||||
|
* @param host The IP/Host Name of the Mongo Server
|
||||||
|
* @param port The Port of the Mongo Server
|
||||||
|
* @param username The Username of the user with the appropriate permissions
|
||||||
|
* @param password The Password of the user with the appropriate permissions
|
||||||
|
* @param databaseName The database to use.
|
||||||
|
* @param authDb The database to authenticate with.
|
||||||
|
*/
|
||||||
|
public MongoConnectionHandler(@NotNull String host, @NotNull Integer port, @NotNull String username, @NotNull String password, @NotNull String databaseName, @NotNull String authDb) {
|
||||||
|
final ServerAddress serverAddress = new ServerAddress(host, port);
|
||||||
|
final MongoCredential credential = MongoCredential.createCredential(username, authDb, password.toCharArray());
|
||||||
|
|
||||||
|
final MongoClientSettings settings = MongoClientSettings.builder()
|
||||||
|
.credential(credential)
|
||||||
|
.applyToClusterSettings(builder -> builder.hosts(Collections.singletonList(serverAddress)))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
this.mongoClient = MongoClients.create(settings);
|
||||||
|
this.database = mongoClient.getDatabase(databaseName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the connection with the database
|
||||||
|
*/
|
||||||
|
public void closeConnection() {
|
||||||
|
if (this.mongoClient != null) {
|
||||||
|
this.mongoClient.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -124,7 +124,9 @@ public abstract class EventListener {
|
|||||||
.forEach(user -> {
|
.forEach(user -> {
|
||||||
plugin.lockPlayer(user.getUuid());
|
plugin.lockPlayer(user.getUuid());
|
||||||
plugin.getDataSyncer().saveData(
|
plugin.getDataSyncer().saveData(
|
||||||
user, user.createSnapshot(DataSnapshot.SaveCause.SERVER_SHUTDOWN), null
|
user,
|
||||||
|
user.createSnapshot(DataSnapshot.SaveCause.SERVER_SHUTDOWN),
|
||||||
|
(saved, data) -> plugin.getRedisManager().clearUserData(saved)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,33 +1,33 @@
|
|||||||
locales:
|
locales:
|
||||||
synchronization_complete: '[⏵資料已同步!](#00fb9a)'
|
synchronization_complete: '[⏵ 資料已同步!](#00fb9a)'
|
||||||
synchronization_failed: '[⏵ 無法同步您的資料! 請聯繫管理員](#ff7e5e)'
|
synchronization_failed: '[⏵ 無法同步您的資料! 請聯繫管理員](#ff7e5e)'
|
||||||
inventory_viewer_menu_title: '&0%1% 的背包'
|
inventory_viewer_menu_title: '&0%1% 的背包'
|
||||||
ender_chest_viewer_menu_title: '&0%1% 的終界箱'
|
ender_chest_viewer_menu_title: '&0%1% 的終界箱'
|
||||||
inventory_viewer_opened: '[查看](#00fb9a) [%1%](#00fb9a bold) [於 ⌚ %2% 的背包快照資料](#00fb9a)'
|
inventory_viewer_opened: '[查看備份](#00fb9a) [%1%](#00fb9a bold) [於 ⌚ %2% 的背包備份](#00fb9a)'
|
||||||
ender_chest_viewer_opened: '[查看](#00fb9a) [%1%](#00fb9a bold) [於 ⌚ %2% 的終界箱快照資料](#00fb9a)'
|
ender_chest_viewer_opened: '[查看備份](#00fb9a) [%1%](#00fb9a bold) [於 ⌚ %2% 的終界箱備份](#00fb9a)'
|
||||||
data_update_complete: '[🔔 你的資料已更新!](#00fb9a)'
|
data_update_complete: '[🔔 你的數據資料已更新!](#00fb9a)'
|
||||||
data_update_failed: '[🔔 無法更新您的資料! 請聯繫管理員](#ff7e5e)'
|
data_update_failed: '[🔔 無法更新你的數據資料! 請聯繫管理員](#ff7e5e)'
|
||||||
user_registration_complete: '[⭐ User registration complete!](#00fb9a)'
|
user_registration_complete: '[⭐ 用戶註冊完成!](#00fb9a)'
|
||||||
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_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_timestamp: '[⌚ %1%](#ffc43b-#f5c962 show_text=&7快照時間:\n&8何時保存的資料)'
|
data_manager_timestamp: '[⌚ %1%](#ffc43b-#f5c962 show_text=&7備份時間:\n&8數據保存時間)'
|
||||||
data_manager_pinned: '[※ 被標記的快照](#d8ff2b show_text=&7標記:\n&8此快照資料不會自動輪換更新)'
|
data_manager_pinned: '[※ 被標記的備份](#d8ff2b show_text=&7已標記:\n&8此玩家數據備份不會按照備份時間自動排序)'
|
||||||
data_manager_cause: '[⚑ %1%](#23a825-#36f539 show_text=&7保存原因:\n&8保存此快照的原因)'
|
data_manager_cause: '[⚑ %1%](#23a825-#36f539 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_server: '[☁ %1%](#ff87b3-#f5538e 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_size: '[⏏ %1%](color=#62a9f5-#7ab8fa show_text=&7備份大小:\n&8備份的估計文件大小(以KiB為單位))\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_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_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_advancements_statistics: '[⭐ 成就: %1%](color=#ffc43b-#f5c962 show_text=&7已獲得的成就:\n&8%2%) [⌛ 遊玩時間: %3%小時](color=#62a9f5-#7ab8fa show_text=&7在遊戲内遊玩的時間\n&8⚠ 基於遊戲内的統計訊息)\n'
|
||||||
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_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_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_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_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_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_advancements_preview_remaining: '還有 %1% …'
|
data_manager_advancements_preview_remaining: '以及其他 %1%…'
|
||||||
data_list_title: '[%1% 的玩家資料快照:](#00fb9a) [(%2%-%3% 共](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n'
|
data_list_title: '[%1%的玩家數據備份:](#00fb9a) [(%2%-%3% of](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n'
|
||||||
data_list_item: '[%1%](gray show_text=&7User Data Snapshot for %2%&8⚡ %4% run_command=/userdata view %2% %3%) [%5%](#d8ff2b show_text=&7Pinned:\n&8Pinned snapshots won''t be automatically rotated. run_command=/userdata view %2% %3%) [%6%](color=#ffc43b-#f5c962 show_text=&7Version timestamp:&7\n&8When the data was saved\n&8%7% run_command=/userdata view %2% %3%) [⚑ %8%](#23a825-#36f539 show_text=&7Save cause:\n&8What caused the data to be saved run_command=/userdata view %2% %3%) [⏏ %9%](color=#62a9f5-#7ab8fa show_text=&7Snapshot size:&7\n&8Estimated file size of the snapshot (in KiB) run_command=/userdata view %2% %3%)'
|
data_list_item: '[%1%](gray show_text=&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_deleted: '[❌ 成功刪除:](#00fb9a) [%3%](#00fb9a show_text=&7玩家 UUID:\n&8%4%) [的快照:](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\n&8%2%)'
|
data_deleted: '[❌ 成功刪除玩家](#00fb9a) [%3%](#00fb9a show_text=&7玩家 UUID:\n&7%4%) [的數據備份](#00fb9a) [%1%.](#00fb9a show_text=&7備份版本UUID:\n&7%2%)'
|
||||||
data_restored: '[⏪ 成功將玩家](#00fb9a) [%1%](#00fb9a show_text=&7玩家 UUID:\n&8%2%)[的資料恢復為 快照:](#00fb9a) [%3%.](#00fb9a show_text=&7Version UUID:\n&8%4%)'
|
data_restored: '[⏪ 成功恢復玩家](#00fb9a) [%1%](#00fb9a show_text=&7玩家 UUID:\n&7%2%)[的數據備份](#00fb9a) [%3%.](#00fb9a show_text=&7備份版本UUID:\n&7%4%)'
|
||||||
data_pinned: '[※ 成功標記](#00fb9a) [%3%](#00fb9a show_text=&7玩家 UUID:\n&8%4%) [的快照:](#00fb9a) [%1%](#00fb9a show_text=&7Version 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_unpinned: '[※ 成功解除](#00fb9a) [%3%](#00fb9a show_text=&7玩家 UUID:\n&8%4%) [快照:](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\n&8%2%) [的標記](#00fb9a)'
|
data_unpinned: '[※ 成功取消標記玩家](#00fb9a) [%3%](#00fb9a show_text=&7玩家 UUID:\n&8%4%) [的數據備份](#00fb9a) [%1%.](#00fb9a show_text=&7備份版本UUID:\n&8%2%)'
|
||||||
data_dumped: '[☂ 成功將 %2% 資料快照 %1% 儲存至:](#00fb9a) &7%3%'
|
data_dumped: '[☂ 已成功將 %1% 的玩家數據快照 %2% 儲存至:](#00fb9a) &7%3%'
|
||||||
list_footer: '\n%1%[頁面](#00fb9a) [%2%](#00fb9a)/[%3%](#00fb9a)%4% %5%'
|
list_footer: '\n%1%[頁數](#00fb9a) [%2%](#00fb9a)/[%3%](#00fb9a)%4% %5%'
|
||||||
list_previous_page_button: '[◀](white show_text=&7查看上一頁 run_command=%2% %1%) '
|
list_previous_page_button: '[◀](white show_text=&7查看上一頁 run_command=%2% %1%) '
|
||||||
list_next_page_button: ' [▶](white show_text=&7查看下一頁 run_command=%2% %1%)'
|
list_next_page_button: ' [▶](white show_text=&7查看下一頁 run_command=%2% %1%)'
|
||||||
list_page_jumpers: '(%1%)'
|
list_page_jumpers: '(%1%)'
|
||||||
@@ -35,29 +35,29 @@ locales:
|
|||||||
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) [| 發現可用的新版本: v%1% (running: v%2%).](#ff7e5e)'
|
update_available: '[HuskSync](#ff7e5e bold) [| 發現可用的新版本:v%1%(當前版本: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[⚠ 確保所有伺服器上的配置文件都是最新的!](#00fb9a)\n[需要重新啟動配置更改才能生效.](#00fb9a italic)'
|
||||||
system_status_header: '[HuskSync](#00fb9a bold) [| System status report:](#00fb9a)'
|
system_status_header: '[HuskSync](#00fb9a bold) [| 系統狀態報告:](#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&點擊建議 suggest_command=%1%)'
|
||||||
error_invalid_player: '[錯誤:](#ff3300) [找不到這位玩家](#ff7e5e)'
|
error_invalid_player: '[錯誤:](#ff3300) [找不到這位玩家.](#ff7e5e)'
|
||||||
error_no_permission: '[錯誤:](#ff3300) [您沒有權限執行這個指令](#ff7e5e)'
|
error_no_permission: '[錯誤:](#ff3300) [您沒有權限執行這個指令](#ff7e5e)'
|
||||||
error_console_command_only: '[錯誤:](#ff3300) [該指令只能透過 控制台 執行](#ff7e5e)'
|
error_console_command_only: '[錯誤:](#ff3300) [該指令只能透過 控制台 執行](#ff7e5e)'
|
||||||
error_in_game_command_only: '[錯誤:](#ff3300) [該指令只能在遊戲內執行](#ff7e5e)'
|
error_in_game_command_only: '錯誤: 該指令只能在遊戲內執行.'
|
||||||
error_no_data_to_display: '[錯誤:](#ff3300) [找不到任何可顯示的用戶資訊.](#ff7e5e)'
|
error_no_data_to_display: '[錯誤:](#ff3300) [找不到任何可顯示的玩家數據.](#ff7e5e)'
|
||||||
error_invalid_version_uuid: '[錯誤:](#ff3300) [找不到正確的 Version UUID.](#ff7e5e)'
|
error_invalid_version_uuid: '[錯誤:](#ff3300) [找不到該版本UUID的任何玩家數據.](#ff7e5e)'
|
||||||
husksync_command_description: 'Manage the HuskSync plugin'
|
husksync_command_description: '管理HuskSync插件'
|
||||||
userdata_command_description: 'View, manage & restore player userdata'
|
userdata_command_description: '查看、管理和恢復玩家用户數據'
|
||||||
inventory_command_description: 'View & edit a player''s inventory'
|
inventory_command_description: '查看和編輯玩家的背包'
|
||||||
enderchest_command_description: 'View & edit a player''s Ender Chest'
|
enderchest_command_description: '查看和編輯玩家的終界箱'
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ This will walk you through installing HuskSync on your network of Spigot servers
|
|||||||
## Requirements
|
## Requirements
|
||||||
> **Note:** If the plugin fails to load, please check that you are not running an [incompatible version combination](Unsupported-Versions)
|
> **Note:** If the plugin fails to load, please check that you are not running an [incompatible version combination](Unsupported-Versions)
|
||||||
|
|
||||||
* A MySQL Database (v8.0+)
|
* A MySQL Database (v8.0+) (or MongoDB Database)
|
||||||
* A Redis Database (v5.0+) — see [[FAQs]] for more details.
|
* A Redis Database (v5.0+) — see [[FAQs]] for more details.
|
||||||
* Any number of Spigot servers, connected by a BungeeCord or Velocity-based proxy (Minecraft v1.17.1+, running Java 17+)
|
* Any number of Spigot servers, connected by a BungeeCord or Velocity-based proxy (Minecraft v1.17.1+, running Java 17+)
|
||||||
|
|
||||||
@@ -15,11 +15,19 @@ This will walk you through installing HuskSync on your network of Spigot servers
|
|||||||
- Start, then stop every server to let HuskSync generate the [[config file]].
|
- Start, then stop every server to let HuskSync generate the [[config file]].
|
||||||
- HuskSync will throw an error in the console and disable itself as it is unable to connect to the database. You haven't set the credentials yet, so this is expected.
|
- HuskSync will throw an error in the console and disable itself as it is unable to connect to the database. You haven't set the credentials yet, so this is expected.
|
||||||
- Advanced users: If you'd prefer, you can just create one config.yml file and create symbolic links in each `/plugins/HuskSync/` folder to it to make updating it easier.
|
- Advanced users: If you'd prefer, you can just create one config.yml file and create symbolic links in each `/plugins/HuskSync/` folder to it to make updating it easier.
|
||||||
### 3. Enter MySQL & Redis database credentials
|
### 3. Enter Mysql & Redis database credentials
|
||||||
- Navigate to the HuskSync config file on each server (`~/plugins/HuskSync/config.yml`)
|
- Navigate to the HuskSync config file on each server (`~/plugins/HuskSync/config.yml`)
|
||||||
- Under `credentials` in the `database` section, enter the credentials of your MySQL Database. You shouldn't touch the `connection_pool` properties.
|
- Under `credentials` in the `database` section, enter the credentials of your MySQL Database. You shouldn't touch the `connection_pool` properties.
|
||||||
- Under `credentials` in the `redis` section, enter the credentials of your Redis Database. If your Redis server doesn't have a password, leave the password blank as it is.
|
- Under `credentials` in the `redis` section, enter the credentials of your Redis Database. If your Redis server doesn't have a password, leave the password blank as it is.
|
||||||
- Unless you want to have multiple clusters of servers within your network, each with separate user data, you should not change the value of `cluster_id`.
|
- Unless you want to have multiple clusters of servers within your network, each with separate user data, you should not change the value of `cluster_id`.
|
||||||
|
<details>
|
||||||
|
<summary><b>For MongoDB Users</b></summary>
|
||||||
|
|
||||||
|
- Navigate to the HuskSync config file on each server (`~/plugins/HuskSync/config.yml`)
|
||||||
|
- Under `credentials` in the `database` section, enter the credentials of your MongoDB Database. You shouldn't touch the `connection_pool` properties.
|
||||||
|
- Be sure to fill in the `mongo_auth_db` field with the database that the username and password is authenticated in. (In most cases this will {and should be} be the same database as the database your trying to connect to.)
|
||||||
|
</details>
|
||||||
|
|
||||||
### 4. Set server names in server.yml files
|
### 4. Set server names in server.yml files
|
||||||
- Navigate to the HuskSync server name file on each server (`~/plugins/HuskSync/server.yml`)
|
- Navigate to the HuskSync server name file on each server (`~/plugins/HuskSync/server.yml`)
|
||||||
- Set the `name:` of the server in this file to the ID of this server as defined in the config of your proxy (e.g., if this is the "hub" server you access with `/server hub`, put `'hub'` here)
|
- Set the `name:` of the server in this file to the ID of this server as defined in the config of your proxy (e.g., if this is the "hub" server you access with `/server hub`, put `'hub'` here)
|
||||||
|
|||||||
@@ -3,11 +3,12 @@ org.gradle.jvmargs='-Dfile.encoding=UTF-8'
|
|||||||
org.gradle.daemon=true
|
org.gradle.daemon=true
|
||||||
javaVersion=17
|
javaVersion=17
|
||||||
|
|
||||||
plugin_version=3.3.3
|
plugin_version=3.4
|
||||||
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.3.0
|
mysql_driver_version=8.3.0
|
||||||
mariadb_driver_version=3.3.2
|
mariadb_driver_version=3.3.2
|
||||||
|
mongodb_driver_version=3.12.14
|
||||||
snappy_version=1.1.10.5
|
snappy_version=1.1.10.5
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ shadowJar {
|
|||||||
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 'de.exlll', 'net.william278.huskclaims.libraries'
|
relocate 'de.exlll', 'net.william278.husksync.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'
|
||||||
|
|||||||
@@ -3,4 +3,5 @@ libraries:
|
|||||||
- 'redis.clients:jedis:${jedis_version}'
|
- 'redis.clients:jedis:${jedis_version}'
|
||||||
- 'com.mysql:mysql-connector-j:${mysql_driver_version}'
|
- 'com.mysql:mysql-connector-j:${mysql_driver_version}'
|
||||||
- 'org.mariadb.jdbc:mariadb-java-client:${mariadb_driver_version}'
|
- 'org.mariadb.jdbc:mariadb-java-client:${mariadb_driver_version}'
|
||||||
|
- 'org.mongodb:mongodb-driver:${mongodb_driver_version}'
|
||||||
- 'org.xerial.snappy:snappy-java:${snappy_version}'
|
- 'org.xerial.snappy:snappy-java:${snappy_version}'
|
||||||
Reference in New Issue
Block a user