9
0
mirror of https://github.com/WiIIiam278/HuskSync.git synced 2025-12-26 01:59:20 +00:00

Compare commits

...

38 Commits
1.3 ... 1.3.2

Author SHA1 Message Date
William
2d8f139940 Encode Javadocs with UTF-8 2022-02-16 00:23:48 +00:00
William
07452083cb Merge remote-tracking branch 'origin/master' 2022-02-12 15:36:32 +00:00
William
55ea6bc391 Add spanish locale credit 2022-02-12 15:36:27 +00:00
William
742a033cf7 Add spanish (es-es) localisation by anchelthe 2022-02-12 15:20:12 +00:00
William
a3d745898e Add localisation credit 2022-02-06 14:59:44 +00:00
William
25c4553dd8 Merge branch 'ja-jp' 2022-02-06 14:57:34 +00:00
William
049dcbe589 Fix rare EndOfStream exception with Jedis listener 2022-02-06 14:56:48 +00:00
Namiu/うにたろう
3ffa2dc0ca Create ja-jp.yml
Add japanese language file
2022-02-06 17:18:00 +09:00
William
9bf0fe7bb9 Use a continuous connection for pub/sub to avoid EndOfStreamException and increase exception logging verbosity 2022-02-05 15:15:04 +00:00
William
4ab5070043 Comment typo 2022-02-03 19:36:25 +00:00
William
1664f1bf66 Upgrade bStats to 3.0.0 2022-02-03 19:31:22 +00:00
Harvels X
1a45100907 Add Gradle parallel execution of tasks; 2022-02-02 18:10:41 +03:00
Harvels X
50b07721d9 Resolve conversations https://github.com/WiIIiam278/HuskSync/pull/16; 2022-02-02 17:50:11 +03:00
Harvels X
b450910b5a Fix meta separator in VersionUtils.toString; 2022-01-31 20:14:36 +03:00
Harvels X
28c14ed393 Edit: fix UpdateChecker version check;
Add: version utils;
2022-01-31 20:03:24 +03:00
Harvels X
3c50245540 Edit: relocations & excludes;
Refactoring modules configuration;
2022-01-31 18:23:01 +03:00
Harvels X
6ea8cdb75c Edit: move shadow plugin to global;
Add: shading main modules;
2022-01-31 18:21:31 +03:00
Harvels X
fd42bc99be Edit: move plugin manifests;
Edit: retrieve a version of velocity;
2022-01-31 18:18:10 +03:00
Harvels X
545c0896f0 Add: static project properties; 2022-01-31 16:02:25 +03:00
Harvels X
d67d5b64da Edit: remove use shadowing for apis; 2022-01-31 16:01:32 +03:00
Harvels X
83ddc76075 Edit: cleanup & move to submodule;
Add: filter resource for all projects & use maven style;
Edit: add revision or build number in project version;
2022-01-31 16:00:08 +03:00
William
06e72f0831 Merge remote-tracking branch 'origin/master' 2022-01-27 16:20:18 +00:00
William
c439ad59ac Fixed current timestamp being generated being incorrect 2022-01-27 16:19:56 +00:00
William
aa3e73ea33 Add velocity metrics to README.md 2022-01-24 13:49:40 +00:00
William
37520991e5 Conformity to Bukkit API conventions 2022-01-22 22:41:39 +00:00
William
804f156027 Bump version number 2022-01-22 22:38:37 +00:00
William
56ecb7f76a Use lowest event priority so HuskSync fires first 2022-01-22 22:37:16 +00:00
William
7a89ffdf35 Make event priority LOWEST 2022-01-22 22:32:44 +00:00
William
6719858de1 Add SSL connection option for Redis 2022-01-21 00:57:16 +00:00
William
920d2582f5 Update Redis initialization handling now that it is multithreaded 2022-01-20 18:36:33 +00:00
William
7d46ce076b Authenticate when creating the ConnectionPool 2022-01-20 18:17:25 +00:00
William
027ee0dbbb Escape version string 2022-01-19 17:51:36 +00:00
William
93be26a946 Use JedisPool instead of single Jedis connection 2022-01-19 17:29:25 +00:00
William
4ec4ba9a1e Fix description 2022-01-19 13:56:29 +00:00
William
6d31d28f47 Bump Jedis, fix status message missing a newline 2022-01-19 13:55:03 +00:00
William
de3838873e Merge remote-tracking branch 'origin/master' 2022-01-13 18:01:28 +00:00
William
f01bb7c082 Update author URL 2022-01-13 18:01:23 +00:00
William
051e2c5b72 Update README.md 2022-01-07 20:52:01 +00:00
39 changed files with 419 additions and 251 deletions

View File

@@ -149,7 +149,7 @@ Or, with Gradle, add the dependency like so to your build.gradle:
``` ```
Then add the dependency as follows. Replace `version` with the latest version of HuskSync: [![](https://jitpack.io/v/WiIIiam278/HuskSync.svg)](https://jitpack.io/#WiIIiam278/HuskSync) Then add the dependency as follows. Replace `version` with the latest version of HuskSync: [![](https://jitpack.io/v/WiIIiam278/HuskSync.svg)](https://jitpack.io/#WiIIiam278/HuskSync)
``` ```
dependencies { dependencies {
compileOnly 'com.github.WiIIiam278:HuskSync:version' compileOnly 'com.github.WiIIiam278:HuskSync:version'
} }
``` ```
@@ -160,7 +160,7 @@ Then add the dependency as follows. Replace `version` with the latest version of
#### Fetching player data on demand #### Fetching player data on demand
To fetch PlayerData from a UUID as you need it, create an instance of the HuskSyncAPI class and use the `#getPlayerData` method. Note that data returned in this method is only the data from the central cache. That is to say, if the player is online, the data returned in this way will not necessarily be the same as the player's actual current data. To fetch PlayerData from a UUID as you need it, create an instance of the HuskSyncAPI class and use the `#getPlayerData` method. Note that data returned in this method is only the data from the central cache. That is to say, if the player is online, the data returned in this way will not necessarily be the same as the player's actual current data.
``` ```java
HuskSyncAPI huskSyncApi = HuskSyncAPI.getInstance(); HuskSyncAPI huskSyncApi = HuskSyncAPI.getInstance();
try { try {
CompletableFuture<PlayerData> playerDataCompletableFuture = huskSyncApi.getPlayerData(playerUUID); CompletableFuture<PlayerData> playerDataCompletableFuture = huskSyncApi.getPlayerData(playerUUID);
@@ -175,14 +175,14 @@ try {
#### Getting ItemStacks and usable data from PlayerData #### Getting ItemStacks and usable data from PlayerData
Use the static methods provided in the [DataSerializer class](https://javadoc.jitpack.io/com/github/WiIIiam278/HuskSync/latest/javadoc/me/william278/husksync/bukkit/data/DataSerializer.html). For instance, to get a player's inventory as an `ItemStack[]` from a `PlayerData` object. Use the static methods provided in the [DataSerializer class](https://javadoc.jitpack.io/com/github/WiIIiam278/HuskSync/latest/javadoc/me/william278/husksync/bukkit/data/DataSerializer.html). For instance, to get a player's inventory as an `ItemStack[]` from a `PlayerData` object.
``` ```java
ItemStack[] inventoryItems = DataSerializer.serializeInventory(playerData.getSerializedInventory()); ItemStack[] inventoryItems = DataSerializer.serializeInventory(playerData.getSerializedInventory());
ItemStack[] enderChestItems = DataSerializer.serializeInventory(playerData.getSerializedEnderChest()); ItemStack[] enderChestItems = DataSerializer.serializeInventory(playerData.getSerializedEnderChest());
``` ```
#### Updating PlayerData #### Updating PlayerData
You can then update PlayerData back to the central cache using the `HuskSyncAPI#updatePlayerData(playerData)` method. For example: You can then update PlayerData back to the central cache using the `HuskSyncAPI#updatePlayerData(playerData)` method. For example:
``` ```java
// Update a value in the player data object // Update a value in the player data object
playerData.setHealth(20); playerData.setHealth(20);
try { try {
@@ -215,10 +215,11 @@ Then, to build the plugin, run the following in the root of the repository:
This plugin uses bStats to provide me with metrics about its usage: This plugin uses bStats to provide me with metrics about its usage:
* [View Bukkit metrics](https://bstats.org/plugin/bukkit/HuskSync%20-%20Bukkit/13140) * [View Bukkit metrics](https://bstats.org/plugin/bukkit/HuskSync%20-%20Bukkit/13140)
* [View BungeeCord metrics](https://bstats.org/plugin/bungeecord/HuskSync%20-%20BungeeCord/13141) * [View BungeeCord metrics](https://bstats.org/plugin/bungeecord/HuskSync%20-%20BungeeCord/13141)
* [View Velocity metrics](https://bstats.org/plugin/velocity/HuskSync%20-%20Velocity/13489)
You can turn metric collection off by navigating to `plugins/bStats/config.yml` and editing the config to disable plugin metrics. You can turn metric collection off by navigating to `plugins/bStats/config.yml` and editing the config to disable plugin metrics.
## Support ## Support
* Report bugs: [Click here](https://github.com/WiIIiam278/HuskSync/issues) * Report bugs: [Click here](https://github.com/WiIIiam278/HuskSync/issues)
* Discord support: Join the [HuskHelp Discord](https://discord.gg/tVYhJfyDWG)! * Discord support: Join the [HuskHelp Discord](https://discord.gg/tVYhJfyDWG)!
* Proof of purchase is required for support. * Proof of purchase is required for support.

View File

@@ -1,35 +1,40 @@
//file:noinspection GroovyAssignabilityCheck
plugins {
id 'java-library'
id 'maven-publish'
}
dependencies { dependencies {
implementation project(':common') compileOnly project(path: ':common', configuration: 'shadow')
compileOnly 'org.spigotmc:spigot-api:1.16.5-R0.1-SNAPSHOT' compileOnly 'org.spigotmc:spigot-api:1.16.5-R0.1-SNAPSHOT'
compileOnly 'org.jetbrains:annotations:22.0.0' compileOnly 'org.jetbrains:annotations:22.0.0'
} }
publishing {
publications {
mavenJava(MavenPublication) {
shadow.component(it)
afterEvaluate {
artifact javadocsJar
}
}
}
repositories {
mavenLocal()
}
}
shadowJar {
classifier = null
relocate ':common', 'me.william278.husksync'
}
repositories { repositories {
mavenCentral() mavenCentral()
maven { url 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/' } maven { url 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/' }
} }
afterEvaluate {
publishing {
publications {
maven(MavenPublication) {
groupId = "${rootProject.group}.${rootProject.name.toLowerCase()}"
artifactId = project.name
from components.java
artifact javadocsJar
}
}
repositories {
mavenLocal()
}
}
}
task javadocs(type: Javadoc) { task javadocs(type: Javadoc) {
options.encoding = 'UTF-8'
options.addStringOption('Xdoclint:none', '-quiet') options.addStringOption('Xdoclint:none', '-quiet')
source = project(':common').sourceSets.main.allJava source = project(':common').sourceSets.main.allJava
source += project(':api').sourceSets.main.allJava source += project(':api').sourceSets.main.allJava
@@ -39,6 +44,6 @@ task javadocs(type: Javadoc) {
} }
task javadocsJar(type: Jar, dependsOn: javadocs) { task javadocsJar(type: Jar, dependsOn: javadocs) {
classifier = 'javadoc' archiveClassifier.set 'javadoc'
from javadocs.destinationDir from javadocs.destinationDir
} }

View File

@@ -1,33 +1,26 @@
buildscript {
repositories {
mavenCentral()
}
}
plugins { plugins {
id 'com.github.johnrengelman.shadow' version '7.1.0' apply false id 'com.github.johnrengelman.shadow' version '7.1.0'
id 'org.ajoberstar.grgit' version '4.1.1'
id 'java' id 'java'
} }
allprojects { group 'me.william278'
group 'me.William278' version "$ext.plugin_version+${versionMetadata()}"
version '1.3'
compileJava { options.encoding = 'UTF-8' } ext {
tasks.withType(JavaCompile) { options.encoding = 'UTF-8' } set 'version', version.toString()
javadoc { options.encoding = 'UTF-8' }
} }
logger.lifecycle('Building HuskSync v' + version.toString()) import org.apache.tools.ant.filters.ReplaceTokens
subprojects { allprojects {
apply plugin: 'com.github.johnrengelman.shadow' apply plugin: 'com.github.johnrengelman.shadow'
apply plugin: 'java' apply plugin: 'java'
apply plugin: 'maven-publish'
compileJava { compileJava.options.encoding = 'UTF-8'
options.release = 16 javadoc.options.encoding = 'UTF-8'
}
compileJava.options.release.set 16
repositories { repositories {
mavenLocal() mavenLocal()
@@ -39,4 +32,40 @@ subprojects {
maven { url 'https://repo.alessiodp.com/releases/' } maven { url 'https://repo.alessiodp.com/releases/' }
maven { url 'https://jitpack.io' } maven { url 'https://jitpack.io' }
} }
dependencies {
implementation('redis.clients:jedis:4.1.1') {
//noinspection GroovyAssignabilityCheck
exclude module: 'slf4j-api'
}
}
processResources {
filter ReplaceTokens as Class, beginToken: '${', endToken: '}',
tokens: rootProject.ext.properties
}
}
subprojects {
version rootProject.version
archivesBaseName = "${rootProject.name}-${project.name.capitalize()}"
if (['bukkit', 'bungeecord', 'velocity', 'plugin'].contains(project.name)) {
shadowJar {
destinationDirectory.set(file("$rootDir/target"))
archiveClassifier.set('')
}
jar.dependsOn shadowJar
clean.delete "$rootDir/target"
}
}
logger.lifecycle("Building HuskSync ${version} by William278")
@SuppressWarnings('GrMethodMayBeStatic')
def versionMetadata() {
if (grgit == null) {
return System.getenv("GITHUB_RUN_NUMBER") ? 'build.' + System.getenv("GITHUB_RUN_NUMBER") : 'unknown'
}
return 'rev.' + grgit.head().abbreviatedId + (grgit.status().clean ? '' : '-indev')
} }

View File

@@ -1,10 +1,8 @@
dependencies { dependencies {
compileOnly project(':common') implementation project(':api')
compileOnly project(':api')
implementation project(path: ':common', configuration: 'shadow') implementation project(path: ':common', configuration: 'shadow')
compileOnly 'redis.clients:jedis:3.7.1' implementation 'org.bstats:bstats-bukkit:3.0.0'
implementation 'org.bstats:bstats-bukkit:2.2.1'
implementation 'de.themoep:minedown:1.7.1-SNAPSHOT' implementation 'de.themoep:minedown:1.7.1-SNAPSHOT'
compileOnly 'net.craftersland.data:bridge:4.0.1' compileOnly 'net.craftersland.data:bridge:4.0.1'
@@ -13,8 +11,9 @@ dependencies {
} }
shadowJar { shadowJar {
relocate 'org.bstats', 'me.William278.husksync.libraries.bstats.bukkit' relocate 'de.themoep', 'me.william278.husksync.libraries'
relocate 'de.themoep', 'me.William278.husksync.libraries.minedown.standard' relocate 'org.bstats', 'me.william278.husksync.libraries.bstats'
}
tasks.register('prepareKotlinBuildScriptModel'){} relocate 'redis.clients', 'me.william278.husksync.libraries'
relocate 'org.apache', 'me.william278.husksync.libraries'
}

View File

@@ -17,7 +17,6 @@ import org.bukkit.scheduler.BukkitTask;
import java.io.IOException; import java.io.IOException;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Level; import java.util.logging.Level;
public final class HuskSyncBukkit extends JavaPlugin { public final class HuskSyncBukkit extends JavaPlugin {
@@ -32,6 +31,8 @@ public final class HuskSyncBukkit extends JavaPlugin {
public static BukkitDataCache bukkitCache; public static BukkitDataCache bukkitCache;
public static BukkitRedisListener redisListener;
// Used for establishing a handshake with redis // Used for establishing a handshake with redis
public static UUID serverUUID; public static UUID serverUUID;
@@ -120,11 +121,7 @@ public final class HuskSyncBukkit extends JavaPlugin {
getServer().getPluginManager().registerEvents(new BukkitEventListener(), this); getServer().getPluginManager().registerEvents(new BukkitEventListener(), this);
// Initialize the redis listener // Initialize the redis listener
if (!new BukkitRedisListener().isActiveAndEnabled) { redisListener = new BukkitRedisListener();
getPluginLoader().disablePlugin(this);
getLogger().severe("Failed to initialize Redis; disabling HuskSync (" + getServer().getName() + ") v" + getDescription().getVersion());
return;
}
// Ensure redis is connected; establish a handshake // Ensure redis is connected; establish a handshake
establishRedisHandshake(); establishRedisHandshake();

View File

@@ -12,6 +12,7 @@ public class ConfigLoader {
Settings.redisHost = config.getString("redis_settings.host", "localhost"); Settings.redisHost = config.getString("redis_settings.host", "localhost");
Settings.redisPort = config.getInt("redis_settings.port", 6379); Settings.redisPort = config.getInt("redis_settings.port", 6379);
Settings.redisPassword = config.getString("redis_settings.password", ""); Settings.redisPassword = config.getString("redis_settings.password", "");
Settings.redisSSL = config.getBoolean("redis_settings.use_ssl", false);
Settings.syncInventories = config.getBoolean("synchronisation_settings.inventories", true); Settings.syncInventories = config.getBoolean("synchronisation_settings.inventories", true);
Settings.syncEnderChests = config.getBoolean("synchronisation_settings.ender_chests", true); Settings.syncEnderChests = config.getBoolean("synchronisation_settings.ender_chests", true);

View File

@@ -22,7 +22,7 @@ public class BukkitEventListener implements Listener {
private static final HuskSyncBukkit plugin = HuskSyncBukkit.getInstance(); private static final HuskSyncBukkit plugin = HuskSyncBukkit.getInstance();
@EventHandler @EventHandler(priority = EventPriority.LOWEST)
public void onPlayerQuit(PlayerQuitEvent event) { public void onPlayerQuit(PlayerQuitEvent event) {
// When a player leaves a Bukkit server // When a player leaves a Bukkit server
final Player player = event.getPlayer(); final Player player = event.getPlayer();
@@ -33,13 +33,14 @@ public class BukkitEventListener implements Listener {
return; return;
} }
if (!plugin.isEnabled() || !HuskSyncBukkit.handshakeCompleted || HuskSyncBukkit.isMySqlPlayerDataBridgeInstalled) return; // If the plugin has not been initialized correctly if (!plugin.isEnabled() || !HuskSyncBukkit.handshakeCompleted || HuskSyncBukkit.isMySqlPlayerDataBridgeInstalled)
return; // If the plugin has not been initialized correctly
// Update the player's data // Update the player's data
PlayerSetter.updatePlayerData(player); PlayerSetter.updatePlayerData(player);
} }
@EventHandler @EventHandler(priority = EventPriority.LOWEST)
public void onPlayerJoin(PlayerJoinEvent event) { public void onPlayerJoin(PlayerJoinEvent event) {
if (!plugin.isEnabled()) return; // If the plugin has not been initialized correctly if (!plugin.isEnabled()) return; // If the plugin has not been initialized correctly
@@ -49,7 +50,8 @@ public class BukkitEventListener implements Listener {
// Mark the player as awaiting data fetch // Mark the player as awaiting data fetch
HuskSyncBukkit.bukkitCache.setAwaitingDataFetch(player.getUniqueId()); HuskSyncBukkit.bukkitCache.setAwaitingDataFetch(player.getUniqueId());
if (!HuskSyncBukkit.handshakeCompleted || HuskSyncBukkit.isMySqlPlayerDataBridgeInstalled) return; // If the data handshake has not been completed yet (or MySqlPlayerDataBridge is installed) if (!HuskSyncBukkit.handshakeCompleted || HuskSyncBukkit.isMySqlPlayerDataBridgeInstalled)
return; // If the data handshake has not been completed yet (or MySqlPlayerDataBridge is installed)
// Send a redis message requesting the player data (if they need to) // Send a redis message requesting the player data (if they need to)
if (HuskSyncBukkit.bukkitCache.isPlayerRequestingOnJoin(player.getUniqueId())) { if (HuskSyncBukkit.bukkitCache.isPlayerRequestingOnJoin(player.getUniqueId())) {
@@ -76,7 +78,8 @@ public class BukkitEventListener implements Listener {
@EventHandler @EventHandler
public void onInventoryClose(InventoryCloseEvent event) { public void onInventoryClose(InventoryCloseEvent event) {
if (!plugin.isEnabled() || !HuskSyncBukkit.handshakeCompleted || HuskSyncBukkit.bukkitCache.isAwaitingDataFetch(event.getPlayer().getUniqueId())) return; // If the plugin has not been initialized correctly if (!plugin.isEnabled() || !HuskSyncBukkit.handshakeCompleted || HuskSyncBukkit.bukkitCache.isAwaitingDataFetch(event.getPlayer().getUniqueId()))
return; // If the plugin has not been initialized correctly
// When a player closes an Inventory // When a player closes an Inventory
final Player player = (Player) event.getPlayer(); final Player player = (Player) event.getPlayer();
@@ -95,14 +98,14 @@ public class BukkitEventListener implements Listener {
* Events to cancel if the player has not been set yet * Events to cancel if the player has not been set yet
*/ */
@EventHandler(priority = EventPriority.MONITOR) @EventHandler(priority = EventPriority.HIGHEST)
public void onDropItem(PlayerDropItemEvent event) { public void onDropItem(PlayerDropItemEvent event) {
if (!plugin.isEnabled() || !HuskSyncBukkit.handshakeCompleted || HuskSyncBukkit.bukkitCache.isAwaitingDataFetch(event.getPlayer().getUniqueId())) { if (!plugin.isEnabled() || !HuskSyncBukkit.handshakeCompleted || HuskSyncBukkit.bukkitCache.isAwaitingDataFetch(event.getPlayer().getUniqueId())) {
event.setCancelled(true); // If the plugin / player has not been set event.setCancelled(true); // If the plugin / player has not been set
} }
} }
@EventHandler(priority = EventPriority.MONITOR) @EventHandler(priority = EventPriority.HIGHEST)
public void onPickupItem(EntityPickupItemEvent event) { public void onPickupItem(EntityPickupItemEvent event) {
if (event.getEntity() instanceof Player player) { if (event.getEntity() instanceof Player player) {
if (!plugin.isEnabled() || !HuskSyncBukkit.handshakeCompleted || HuskSyncBukkit.bukkitCache.isAwaitingDataFetch(player.getUniqueId())) { if (!plugin.isEnabled() || !HuskSyncBukkit.handshakeCompleted || HuskSyncBukkit.bukkitCache.isAwaitingDataFetch(player.getUniqueId())) {
@@ -111,28 +114,28 @@ public class BukkitEventListener implements Listener {
} }
} }
@EventHandler(priority = EventPriority.MONITOR) @EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerInteract(PlayerInteractEvent event) { public void onPlayerInteract(PlayerInteractEvent event) {
if (!plugin.isEnabled() || !HuskSyncBukkit.handshakeCompleted || HuskSyncBukkit.bukkitCache.isAwaitingDataFetch(event.getPlayer().getUniqueId())) { if (!plugin.isEnabled() || !HuskSyncBukkit.handshakeCompleted || HuskSyncBukkit.bukkitCache.isAwaitingDataFetch(event.getPlayer().getUniqueId())) {
event.setCancelled(true); // If the plugin / player has not been set event.setCancelled(true); // If the plugin / player has not been set
} }
} }
@EventHandler(priority = EventPriority.MONITOR) @EventHandler(priority = EventPriority.HIGHEST)
public void onBlockPlace(BlockPlaceEvent event) { public void onBlockPlace(BlockPlaceEvent event) {
if (!plugin.isEnabled() || !HuskSyncBukkit.handshakeCompleted || HuskSyncBukkit.bukkitCache.isAwaitingDataFetch(event.getPlayer().getUniqueId())) { if (!plugin.isEnabled() || !HuskSyncBukkit.handshakeCompleted || HuskSyncBukkit.bukkitCache.isAwaitingDataFetch(event.getPlayer().getUniqueId())) {
event.setCancelled(true); // If the plugin / player has not been set event.setCancelled(true); // If the plugin / player has not been set
} }
} }
@EventHandler(priority = EventPriority.MONITOR) @EventHandler(priority = EventPriority.HIGHEST)
public void onBlockBreak(BlockBreakEvent event) { public void onBlockBreak(BlockBreakEvent event) {
if (!plugin.isEnabled() || !HuskSyncBukkit.handshakeCompleted || HuskSyncBukkit.bukkitCache.isAwaitingDataFetch(event.getPlayer().getUniqueId())) { if (!plugin.isEnabled() || !HuskSyncBukkit.handshakeCompleted || HuskSyncBukkit.bukkitCache.isAwaitingDataFetch(event.getPlayer().getUniqueId())) {
event.setCancelled(true); // If the plugin / player has not been set event.setCancelled(true); // If the plugin / player has not been set
} }
} }
@EventHandler(priority = EventPriority.MONITOR) @EventHandler(priority = EventPriority.HIGHEST)
public void onInventoryOpen(InventoryOpenEvent event) { public void onInventoryOpen(InventoryOpenEvent event) {
if (!plugin.isEnabled() || !HuskSyncBukkit.handshakeCompleted || HuskSyncBukkit.bukkitCache.isAwaitingDataFetch(event.getPlayer().getUniqueId())) { if (!plugin.isEnabled() || !HuskSyncBukkit.handshakeCompleted || HuskSyncBukkit.bukkitCache.isAwaitingDataFetch(event.getPlayer().getUniqueId())) {
event.setCancelled(true); // If the plugin / player has not been set event.setCancelled(true); // If the plugin / player has not been set

View File

@@ -26,6 +26,7 @@ public class BukkitRedisListener extends RedisListener {
// Initialize the listener on the bukkit server // Initialize the listener on the bukkit server
public BukkitRedisListener() { public BukkitRedisListener() {
super();
listen(); listen();
} }

View File

@@ -66,7 +66,7 @@ public class PlayerSetter {
private static double getMaxHealth(Player player) { private static double getMaxHealth(Player player) {
double maxHealth = Objects.requireNonNull(player.getAttribute(Attribute.GENERIC_MAX_HEALTH)).getBaseValue(); double maxHealth = Objects.requireNonNull(player.getAttribute(Attribute.GENERIC_MAX_HEALTH)).getBaseValue();
// If the player has additional health bonuses from synchronised potion effects, subtract these from this number as they are synchronised seperately // If the player has additional health bonuses from synchronised potion effects, subtract these from this number as they are synchronised separately
if (player.hasPotionEffect(PotionEffectType.HEALTH_BOOST) && maxHealth > 20D) { if (player.hasPotionEffect(PotionEffectType.HEALTH_BOOST) && maxHealth > 20D) {
PotionEffect healthBoostEffect = player.getPotionEffect(PotionEffectType.HEALTH_BOOST); PotionEffect healthBoostEffect = player.getPotionEffect(PotionEffectType.HEALTH_BOOST);
assert healthBoostEffect != null; assert healthBoostEffect != null;

View File

@@ -1,6 +1,7 @@
package me.william278.husksync.bukkit.util.nms; package me.william278.husksync.bukkit.util.nms;
import me.william278.husksync.util.ThrowSupplier; import me.william278.husksync.util.ThrowSupplier;
import me.william278.husksync.util.VersionUtils;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
public class MinecraftVersionUtils { public class MinecraftVersionUtils {
@@ -8,27 +9,10 @@ public class MinecraftVersionUtils {
public final static String CRAFTBUKKIT_PACKAGE_PATH = Bukkit.getServer().getClass().getPackage().getName(); public final static String CRAFTBUKKIT_PACKAGE_PATH = Bukkit.getServer().getClass().getPackage().getName();
public final static String PACKAGE_VERSION = CRAFTBUKKIT_PACKAGE_PATH.split("\\.")[3]; public final static String PACKAGE_VERSION = CRAFTBUKKIT_PACKAGE_PATH.split("\\.")[3];
public final static String MINECRAFT_PACKAGE = compare("1.17") < 0 ? public final static VersionUtils.Version SERVER_VERSION
= VersionUtils.Version.of(Bukkit.getBukkitVersion().split("-")[0]);
public final static String MINECRAFT_PACKAGE = SERVER_VERSION.compareTo(VersionUtils.Version.of("1.17")) < 0 ?
"net.minecraft.server.".concat(PACKAGE_VERSION) : "net.minecraft.server"; "net.minecraft.server.".concat(PACKAGE_VERSION) : "net.minecraft.server";
public final static String SERVER_VERSION = Bukkit.getBukkitVersion().split("-")[0];
public static int compare(String version) {
if (version == null || SERVER_VERSION == null) return 1;
String[] as = SERVER_VERSION.split("\\.");
String[] bs = version.split("\\.");
int length = Math.max(as.length, bs.length);
for (int i = 0; i < length; i++) {
int a = i < as.length ? Integer.parseInt(as[i]) : 0;
int b = i < bs.length ? Integer.parseInt(bs[i]) : 0;
if (a < b) return -1;
if (a > b) return 1;
}
return 0;
}
public static Class<?> getBukkitClass(String path) { public static Class<?> getBukkitClass(String path) {
return ThrowSupplier.get(() -> Class.forName(CRAFTBUKKIT_PACKAGE_PATH.concat(".").concat(path))); return ThrowSupplier.get(() -> Class.forName(CRAFTBUKKIT_PACKAGE_PATH.concat(".").concat(path)));

View File

@@ -2,6 +2,7 @@ redis_settings:
host: 'localhost' host: 'localhost'
port: 6379 port: 6379
password: '' password: ''
use_ssl: false
synchronisation_settings: synchronisation_settings:
inventories: true inventories: true
ender_chests: true ender_chests: true

View File

@@ -1,7 +1,8 @@
name: HuskSync name: HuskSync
version: @version@ version: ${version}
main: me.william278.husksync.HuskSyncBukkit main: me.william278.husksync.HuskSyncBukkit
api-version: 1.16 api-version: 1.16
author: William278 author: William278
description: 'A modern, cross-server player data synchronization system' description: 'A modern, cross-server player data synchronization system'
website: 'https://william278.net'
softdepend: [MysqlPlayerDataBridge] softdepend: [MysqlPlayerDataBridge]

View File

@@ -1,9 +1,8 @@
dependencies { dependencies {
compileOnly project(':common')
implementation project(path: ':common', configuration: 'shadow') implementation project(path: ':common', configuration: 'shadow')
compileOnly 'redis.clients:jedis:3.7.1' implementation 'com.zaxxer:HikariCP:5.0.1'
implementation 'org.bstats:bstats-bungeecord:2.2.1' implementation 'org.bstats:bstats-bungeecord:3.0.0'
implementation 'de.themoep:minedown:1.7.1-SNAPSHOT' implementation 'de.themoep:minedown:1.7.1-SNAPSHOT'
implementation 'net.byteflux:libby-bungee:1.1.5' implementation 'net.byteflux:libby-bungee:1.1.5'
@@ -11,10 +10,17 @@ dependencies {
} }
shadowJar { shadowJar {
relocate 'com.zaxxer', 'me.William278.husksync.libraries.hikari' relocate 'de.themoep', 'me.william278.husksync.libraries'
relocate 'org.bstats', 'me.William278.husksync.libraries.bstats.bungee' relocate 'net.byteflux', 'me.william278.husksync.libraries'
relocate 'de.themoep', 'me.William278.husksync.libraries.minedown.standard' relocate 'org.bstats', 'me.william278.husksync.libraries.bstats'
relocate 'net.byteflux', 'me.William278.husksync.libraries.libby.bungee'
}
tasks.register('prepareKotlinBuildScriptModel'){} relocate 'redis.clients', 'me.william278.husksync.libraries'
relocate 'org.apache', 'me.william278.husksync.libraries'
relocate 'com.zaxxer', 'me.william278.husksync.libraries'
dependencies {
//noinspection GroovyAssignabilityCheck
exclude dependency(':slf4j-api')
}
}

View File

@@ -3,12 +3,12 @@ package me.william278.husksync;
import me.william278.husksync.bungeecord.command.BungeeCommand; import me.william278.husksync.bungeecord.command.BungeeCommand;
import me.william278.husksync.bungeecord.config.ConfigLoader; import me.william278.husksync.bungeecord.config.ConfigLoader;
import me.william278.husksync.bungeecord.config.ConfigManager; import me.william278.husksync.bungeecord.config.ConfigManager;
import me.william278.husksync.proxy.data.DataManager;
import me.william278.husksync.bungeecord.listener.BungeeEventListener; import me.william278.husksync.bungeecord.listener.BungeeEventListener;
import me.william278.husksync.bungeecord.listener.BungeeRedisListener; import me.william278.husksync.bungeecord.listener.BungeeRedisListener;
import me.william278.husksync.migrator.MPDBMigrator;
import me.william278.husksync.bungeecord.util.BungeeLogger; import me.william278.husksync.bungeecord.util.BungeeLogger;
import me.william278.husksync.bungeecord.util.BungeeUpdateChecker; import me.william278.husksync.bungeecord.util.BungeeUpdateChecker;
import me.william278.husksync.migrator.MPDBMigrator;
import me.william278.husksync.proxy.data.DataManager;
import me.william278.husksync.redis.RedisMessage; import me.william278.husksync.redis.RedisMessage;
import me.william278.husksync.util.Logger; import me.william278.husksync.util.Logger;
import net.byteflux.libby.BungeeLibraryManager; import net.byteflux.libby.BungeeLibraryManager;
@@ -48,6 +48,8 @@ public final class HuskSyncBungeeCord extends Plugin {
public static MPDBMigrator mpdbMigrator; public static MPDBMigrator mpdbMigrator;
public static BungeeRedisListener redisListener;
private Logger logger; private Logger logger;
public Logger getBungeeLogger() { public Logger getBungeeLogger() {
@@ -98,10 +100,7 @@ public final class HuskSyncBungeeCord extends Plugin {
} }
// Initialize the redis listener // Initialize the redis listener
if (!new BungeeRedisListener().isActiveAndEnabled) { redisListener = new BungeeRedisListener();
getBungeeLogger().severe("Failed to initialize Redis; HuskSync will now abort loading itself (" + getProxy().getName() + ") v" + getDescription().getVersion());
return;
}
// Register listener // Register listener
getProxy().getPluginManager().registerListener(this, new BungeeEventListener()); getProxy().getPluginManager().registerListener(this, new BungeeEventListener());

View File

@@ -2,16 +2,16 @@ package me.william278.husksync.bungeecord.command;
import de.themoep.minedown.MineDown; import de.themoep.minedown.MineDown;
import me.william278.husksync.HuskSyncBungeeCord; import me.william278.husksync.HuskSyncBungeeCord;
import me.william278.husksync.Server;
import me.william278.husksync.bungeecord.util.BungeeUpdateChecker;
import me.william278.husksync.proxy.command.HuskSyncCommand;
import me.william278.husksync.util.MessageManager;
import me.william278.husksync.PlayerData; import me.william278.husksync.PlayerData;
import me.william278.husksync.Server;
import me.william278.husksync.Settings; import me.william278.husksync.Settings;
import me.william278.husksync.bungeecord.config.ConfigLoader; import me.william278.husksync.bungeecord.config.ConfigLoader;
import me.william278.husksync.bungeecord.config.ConfigManager; import me.william278.husksync.bungeecord.config.ConfigManager;
import me.william278.husksync.bungeecord.util.BungeeUpdateChecker;
import me.william278.husksync.migrator.MPDBMigrator; import me.william278.husksync.migrator.MPDBMigrator;
import me.william278.husksync.proxy.command.HuskSyncCommand;
import me.william278.husksync.redis.RedisMessage; import me.william278.husksync.redis.RedisMessage;
import me.william278.husksync.util.MessageManager;
import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.connection.ProxiedPlayer;
@@ -301,7 +301,7 @@ public class BungeeCommand extends Command implements TabExecutor, HuskSyncComma
HuskSyncBungeeCord.synchronisedServers)) { HuskSyncBungeeCord.synchronisedServers)) {
ProxyServer.getInstance().getScheduler().runAsync(plugin, () -> ProxyServer.getInstance().getScheduler().runAsync(plugin, () ->
HuskSyncBungeeCord.mpdbMigrator.executeMigrationOperations(HuskSyncBungeeCord.dataManager, HuskSyncBungeeCord.mpdbMigrator.executeMigrationOperations(HuskSyncBungeeCord.dataManager,
HuskSyncBungeeCord.synchronisedServers)); HuskSyncBungeeCord.synchronisedServers, HuskSyncBungeeCord.redisListener));
} }
} }
default -> sender.sendMessage(new MineDown("Error: Invalid argument for migration. Use \"husksync migrate\" to start the process").toComponent()); default -> sender.sendMessage(new MineDown("Error: Invalid argument for migration. Use \"husksync migrate\" to start the process").toComponent());

View File

@@ -42,6 +42,7 @@ public class ConfigLoader {
Settings.redisHost = config.getString("redis_settings.host", "localhost"); Settings.redisHost = config.getString("redis_settings.host", "localhost");
Settings.redisPort = config.getInt("redis_settings.port", 6379); Settings.redisPort = config.getInt("redis_settings.port", 6379);
Settings.redisPassword = config.getString("redis_settings.password", ""); Settings.redisPassword = config.getString("redis_settings.password", "");
Settings.redisSSL = config.getBoolean("redis_settings.use_ssl", false);
Settings.dataStorageType = Settings.DataStorageType.valueOf(config.getString("data_storage_settings.database_type", "sqlite").toUpperCase()); Settings.dataStorageType = Settings.DataStorageType.valueOf(config.getString("data_storage_settings.database_type", "sqlite").toUpperCase());
if (Settings.dataStorageType == Settings.DataStorageType.MYSQL) { if (Settings.dataStorageType == Settings.DataStorageType.MYSQL) {

View File

@@ -9,6 +9,7 @@ import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.PostLoginEvent; import net.md_5.bungee.api.event.PostLoginEvent;
import net.md_5.bungee.api.plugin.Listener; import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.event.EventHandler; import net.md_5.bungee.event.EventHandler;
import net.md_5.bungee.event.EventPriority;
import java.io.IOException; import java.io.IOException;
import java.util.Map; import java.util.Map;
@@ -18,7 +19,7 @@ public class BungeeEventListener implements Listener {
private static final HuskSyncBungeeCord plugin = HuskSyncBungeeCord.getInstance(); private static final HuskSyncBungeeCord plugin = HuskSyncBungeeCord.getInstance();
@EventHandler @EventHandler(priority = EventPriority.LOWEST)
public void onPostLogin(PostLoginEvent event) { public void onPostLogin(PostLoginEvent event) {
final ProxiedPlayer player = event.getPlayer(); final ProxiedPlayer player = event.getPlayer();
ProxyServer.getInstance().getScheduler().runAsync(plugin, () -> { ProxyServer.getInstance().getScheduler().runAsync(plugin, () -> {
@@ -26,7 +27,7 @@ public class BungeeEventListener implements Listener {
HuskSyncBungeeCord.dataManager.ensurePlayerExists(player.getUniqueId(), player.getName()); HuskSyncBungeeCord.dataManager.ensurePlayerExists(player.getUniqueId(), player.getName());
// Get the player's data from SQL // Get the player's data from SQL
final Map<Settings.SynchronisationCluster,PlayerData> data = HuskSyncBungeeCord.dataManager.getPlayerData(player.getUniqueId()); final Map<Settings.SynchronisationCluster, PlayerData> data = HuskSyncBungeeCord.dataManager.getPlayerData(player.getUniqueId());
// Update the player's data from SQL onto the cache // Update the player's data from SQL onto the cache
assert data != null; assert data != null;

View File

@@ -24,6 +24,7 @@ public class BungeeRedisListener extends RedisListener {
// Initialize the listener on the bungee // Initialize the listener on the bungee
public BungeeRedisListener() { public BungeeRedisListener() {
super();
listen(); listen();
} }

View File

@@ -1,5 +1,5 @@
name: HuskSync name: HuskSync
version: @version@ version: ${version}
main: me.william278.husksync.HuskSyncBungeeCord main: me.william278.husksync.HuskSyncBungeeCord
author: William278 author: William278
description: 'A modern, cross-server player data synchronization system' description: 'A modern, cross-server player data synchronization system'

View File

@@ -1,28 +1,7 @@
dependencies { dependencies {
implementation 'redis.clients:jedis:3.7.1' compileOnly 'com.zaxxer:HikariCP:5.0.1'
implementation 'com.zaxxer:HikariCP:5.0.0'
}
import org.apache.tools.ant.filters.ReplaceTokens
task updateVersion(type: Copy) {
from('src/main/resources') {
include 'plugin.yml'
include 'bungee.yml'
}
into 'build/sources/resources/'
filter(ReplaceTokens, tokens: [version: '' + project.version])
}
processResources {
duplicatesStrategy = DuplicatesStrategy.INCLUDE
dependsOn updateVersion
from 'build/sources/resources'
} }
shadowJar { shadowJar {
dependsOn processResources relocate 'com.zaxxer', 'me.william278.husksync.libraries'
// Exclude some unnecessary files
exclude "**/module-info.class"
exclude "module-info.class"
} }

View File

@@ -21,6 +21,7 @@ public class Settings {
public static String redisHost; public static String redisHost;
public static int redisPort; public static int redisPort;
public static String redisPassword; public static String redisPassword;
public static boolean redisSSL;
/* /*
* Bungee / Proxy server-only settings * Bungee / Proxy server-only settings

View File

@@ -6,6 +6,7 @@ import me.william278.husksync.Settings;
import me.william278.husksync.proxy.data.DataManager; import me.william278.husksync.proxy.data.DataManager;
import me.william278.husksync.proxy.data.sql.Database; import me.william278.husksync.proxy.data.sql.Database;
import me.william278.husksync.proxy.data.sql.MySQL; import me.william278.husksync.proxy.data.sql.MySQL;
import me.william278.husksync.redis.RedisListener;
import me.william278.husksync.redis.RedisMessage; import me.william278.husksync.redis.RedisMessage;
import me.william278.husksync.util.Logger; import me.william278.husksync.util.Logger;
@@ -95,7 +96,7 @@ public class MPDBMigrator {
} }
// Carry out the migration // Carry out the migration
public void executeMigrationOperations(DataManager dataManager, HashSet<Server> synchronisedServers) { public void executeMigrationOperations(DataManager dataManager, HashSet<Server> synchronisedServers, RedisListener redisListener) {
// Prepare the target database for insertion // Prepare the target database for insertion
prepareTargetDatabase(dataManager); prepareTargetDatabase(dataManager);
@@ -109,7 +110,7 @@ public class MPDBMigrator {
getExperienceData(); getExperienceData();
// Send the encoded data to the Bukkit servers for conversion // Send the encoded data to the Bukkit servers for conversion
sendEncodedData(synchronisedServers); sendEncodedData(synchronisedServers, redisListener);
} }
// Clear the new database out of current data // Clear the new database out of current data
@@ -200,7 +201,7 @@ public class MPDBMigrator {
} }
} }
private void sendEncodedData(HashSet<Server> synchronisedServers) { private void sendEncodedData(HashSet<Server> synchronisedServers, RedisListener redisListener) {
for (Server processingServer : synchronisedServers) { for (Server processingServer : synchronisedServers) {
if (processingServer.hasMySqlPlayerDataBridge()) { if (processingServer.hasMySqlPlayerDataBridge()) {
for (MPDBPlayerData data : mpdbPlayerData) { for (MPDBPlayerData data : mpdbPlayerData) {

View File

@@ -9,7 +9,6 @@ import me.william278.husksync.util.Logger;
import java.io.File; import java.io.File;
import java.sql.*; import java.sql.*;
import java.time.Instant;
import java.util.*; import java.util.*;
import java.util.logging.Level; import java.util.logging.Level;
@@ -240,7 +239,7 @@ public class DataManager {
try (PreparedStatement statement = connection.prepareStatement( try (PreparedStatement statement = connection.prepareStatement(
"UPDATE " + cluster.dataTableName() + " SET `version_uuid`=?, `timestamp`=?, `inventory`=?, `ender_chest`=?, `health`=?, `max_health`=?, `health_scale`=?, `hunger`=?, `saturation`=?, `saturation_exhaustion`=?, `selected_slot`=?, `status_effects`=?, `total_experience`=?, `exp_level`=?, `exp_progress`=?, `game_mode`=?, `statistics`=?, `is_flying`=?, `advancements`=?, `location`=? WHERE `player_id`=(SELECT `id` FROM " + cluster.playerTableName() + " WHERE `uuid`=?);")) { "UPDATE " + cluster.dataTableName() + " SET `version_uuid`=?, `timestamp`=?, `inventory`=?, `ender_chest`=?, `health`=?, `max_health`=?, `health_scale`=?, `hunger`=?, `saturation`=?, `saturation_exhaustion`=?, `selected_slot`=?, `status_effects`=?, `total_experience`=?, `exp_level`=?, `exp_progress`=?, `game_mode`=?, `statistics`=?, `is_flying`=?, `advancements`=?, `location`=? WHERE `player_id`=(SELECT `id` FROM " + cluster.playerTableName() + " WHERE `uuid`=?);")) {
statement.setString(1, playerData.getDataVersionUUID().toString()); statement.setString(1, playerData.getDataVersionUUID().toString());
statement.setTimestamp(2, new Timestamp(Instant.now().getEpochSecond())); statement.setTimestamp(2, new Timestamp(System.currentTimeMillis()));
statement.setString(3, playerData.getSerializedInventory()); statement.setString(3, playerData.getSerializedInventory());
statement.setString(4, playerData.getSerializedEnderChest()); statement.setString(4, playerData.getSerializedEnderChest());
statement.setDouble(5, playerData.getHealth()); // Health statement.setDouble(5, playerData.getHealth()); // Health
@@ -274,7 +273,7 @@ public class DataManager {
"INSERT INTO " + cluster.dataTableName() + " (`player_id`,`version_uuid`,`timestamp`,`inventory`,`ender_chest`,`health`,`max_health`,`health_scale`,`hunger`,`saturation`,`saturation_exhaustion`,`selected_slot`,`status_effects`,`total_experience`,`exp_level`,`exp_progress`,`game_mode`,`statistics`,`is_flying`,`advancements`,`location`) VALUES((SELECT `id` FROM " + cluster.playerTableName() + " WHERE `uuid`=?),?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);")) { "INSERT INTO " + cluster.dataTableName() + " (`player_id`,`version_uuid`,`timestamp`,`inventory`,`ender_chest`,`health`,`max_health`,`health_scale`,`hunger`,`saturation`,`saturation_exhaustion`,`selected_slot`,`status_effects`,`total_experience`,`exp_level`,`exp_progress`,`game_mode`,`statistics`,`is_flying`,`advancements`,`location`) VALUES((SELECT `id` FROM " + cluster.playerTableName() + " WHERE `uuid`=?),?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);")) {
statement.setString(1, playerData.getPlayerUUID().toString()); statement.setString(1, playerData.getPlayerUUID().toString());
statement.setString(2, playerData.getDataVersionUUID().toString()); statement.setString(2, playerData.getDataVersionUUID().toString());
statement.setTimestamp(3, new Timestamp(Instant.now().getEpochSecond())); statement.setTimestamp(3, new Timestamp(System.currentTimeMillis()));
statement.setString(4, playerData.getSerializedInventory()); statement.setString(4, playerData.getSerializedInventory());
statement.setString(5, playerData.getSerializedEnderChest()); statement.setString(5, playerData.getSerializedEnderChest());
statement.setDouble(6, playerData.getHealth()); // Health statement.setDouble(6, playerData.getHealth()); // Health

View File

@@ -1,9 +1,8 @@
package me.william278.husksync.redis; package me.william278.husksync.redis;
import me.william278.husksync.Settings; import me.william278.husksync.Settings;
import redis.clients.jedis.Jedis; import redis.clients.jedis.*;
import redis.clients.jedis.JedisClientConfig; import redis.clients.jedis.exceptions.JedisConnectionException;
import redis.clients.jedis.JedisPubSub;
import redis.clients.jedis.exceptions.JedisException; import redis.clients.jedis.exceptions.JedisException;
import java.io.IOException; import java.io.IOException;
@@ -16,6 +15,35 @@ public abstract class RedisListener {
*/ */
public boolean isActiveAndEnabled; public boolean isActiveAndEnabled;
/**
* Pool of connections to the Redis server
*/
private static JedisPool jedisPool;
/**
* Creates a new RedisListener and initialises the Redis connection
*/
public RedisListener() {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxIdle(0);
config.setTestOnBorrow(true);
config.setTestOnReturn(true);
if (Settings.redisPassword.isEmpty()) {
jedisPool = new JedisPool(config,
Settings.redisHost,
Settings.redisPort,
0,
Settings.redisSSL);
} else {
jedisPool = new JedisPool(config,
Settings.redisHost,
Settings.redisPort,
0,
Settings.redisPassword,
Settings.redisSSL);
}
}
/** /**
* Handle an incoming {@link RedisMessage} * Handle an incoming {@link RedisMessage}
* *
@@ -31,41 +59,68 @@ public abstract class RedisListener {
*/ */
public abstract void log(Level level, String message); public abstract void log(Level level, String message);
/**
* Fetch a connection to the Redis server from the JedisPool
*
* @return Jedis instance from the pool
*/
public static Jedis getJedisConnection() {
return jedisPool.getResource();
}
/** /**
* Start the Redis listener * Start the Redis listener
*/ */
public final void listen() { public final void listen() {
try (Jedis jedis = new Jedis(Settings.redisHost, Settings.redisPort)) { new Thread(() -> {
final String jedisPassword = Settings.redisPassword; isActiveAndEnabled = true;
jedis.connect(); while (isActiveAndEnabled) {
if (jedis.isConnected()) {
if (!jedisPassword.equals("")) {
jedis.auth(jedisPassword);
}
isActiveAndEnabled = true;
log(Level.INFO, "Enabled Redis listener successfully!");
new Thread(() -> jedis.subscribe(new JedisPubSub() {
@Override
public void onMessage(String channel, String message) {
// Only accept messages to the HuskSync channel
if (!channel.equals(RedisMessage.REDIS_CHANNEL)) {
return;
}
// Handle the message Jedis subscriber;
try { if (Settings.redisPassword.isEmpty()) {
handleMessage(new RedisMessage(message)); subscriber = new Jedis(Settings.redisHost,
} catch (IOException | ClassNotFoundException e) { Settings.redisPort,
log(Level.SEVERE, "Failed to deserialize message target"); 0);
} else {
final JedisClientConfig config = DefaultJedisClientConfig.builder()
.password(Settings.redisPassword)
.timeoutMillis(0).build();
subscriber = new Jedis(Settings.redisHost,
Settings.redisPort,
config);
}
subscriber.connect();
log(Level.INFO, "Enabled Redis listener successfully!");
try {
subscriber.subscribe(new JedisPubSub() {
@Override
public void onMessage(String channel, String message) {
// Only accept messages to the HuskSync channel
if (!channel.equals(RedisMessage.REDIS_CHANNEL)) {
return;
}
// Handle the message
try {
handleMessage(new RedisMessage(message));
} catch (IOException | ClassNotFoundException e) {
log(Level.SEVERE, "Failed to deserialize message target");
}
} }
} }, RedisMessage.REDIS_CHANNEL);
}, RedisMessage.REDIS_CHANNEL), "Redis Subscriber").start(); } catch (JedisConnectionException connectionException) {
} else { log(Level.SEVERE, "A connection exception occurred with the Jedis listener");
isActiveAndEnabled = false; connectionException.printStackTrace();
log(Level.SEVERE, "Failed to initialize the redis listener!"); } catch (JedisException jedisException) {
isActiveAndEnabled = false;
log(Level.SEVERE, "An exception occurred with the Jedis listener");
jedisException.printStackTrace();
} finally {
subscriber.close();
}
} }
} catch (JedisException e) { }, "Redis Subscriber").start();
log(Level.SEVERE, "Failed to establish a connection to the Redis server!");
}
} }
} }

View File

@@ -22,8 +22,9 @@ public class RedisMessage {
/** /**
* Create a new RedisMessage * Create a new RedisMessage
* @param type The type of the message *
* @param target Who will receive this message * @param type The type of the message
* @param target Who will receive this message
* @param messageData The message data elements * @param messageData The message data elements
*/ */
public RedisMessage(MessageType type, MessageTarget target, String... messageData) { public RedisMessage(MessageType type, MessageTarget target, String... messageData) {
@@ -38,6 +39,7 @@ public class RedisMessage {
/** /**
* Get a new RedisMessage from an incoming message string * Get a new RedisMessage from an incoming message string
*
* @param messageString The message string to parse * @param messageString The message string to parse
*/ */
public RedisMessage(String messageString) throws IOException, ClassNotFoundException { public RedisMessage(String messageString) throws IOException, ClassNotFoundException {
@@ -49,6 +51,7 @@ public class RedisMessage {
/** /**
* Returns the full, formatted message string with type, target & data * Returns the full, formatted message string with type, target & data
*
* @return The fully formatted message * @return The fully formatted message
*/ */
private String getFullMessage() throws IOException { private String getFullMessage() throws IOException {
@@ -61,21 +64,18 @@ public class RedisMessage {
* Send the redis message * Send the redis message
*/ */
public void send() throws IOException { public void send() throws IOException {
try (Jedis publisher = new Jedis(Settings.redisHost, Settings.redisPort)) { try (Jedis publisher = RedisListener.getJedisConnection()) {
final String jedisPassword = Settings.redisPassword; publisher.publish(REDIS_CHANNEL, getFullMessage());
publisher.connect(); }
if (!jedisPassword.equals("")) {
publisher.auth(jedisPassword);
}
publisher.publish(REDIS_CHANNEL, getFullMessage());
}
} }
public String getMessageData() { public String getMessageData() {
return messageData; return messageData;
} }
public String[] getMessageDataElements() { return messageData.split(MESSAGE_DATA_SEPARATOR); } public String[] getMessageDataElements() {
return messageData.split(MESSAGE_DATA_SEPARATOR);
}
public MessageType getMessageType() { public MessageType getMessageType() {
return messageType; return messageType;
@@ -173,7 +173,9 @@ public class RedisMessage {
/** /**
* A record that defines the target of a plugin message; a spigot server or the proxy server(s). For Bukkit servers, the name of the server must also be specified * A record that defines the target of a plugin message; a spigot server or the proxy server(s). For Bukkit servers, the name of the server must also be specified
*/ */
public record MessageTarget(Settings.ServerType targetServerType, UUID targetPlayerUUID, String targetClusterId) implements Serializable { } public record MessageTarget(Settings.ServerType targetServerType, UUID targetPlayerUUID,
String targetClusterId) implements Serializable {
}
/** /**
* Deserialize an object from a Base64 string * Deserialize an object from a Base64 string

View File

@@ -16,14 +16,15 @@ public class MessageManager {
public static StringBuilder PLUGIN_INFORMATION = new StringBuilder().append("[HuskSync](#00fb9a bold) [| %proxy_brand% Version %proxy_version% (%bukkit_brand% v%bukkit_version%)](#00fb9a)\n") public static StringBuilder PLUGIN_INFORMATION = new StringBuilder().append("[HuskSync](#00fb9a bold) [| %proxy_brand% Version %proxy_version% (%bukkit_brand% v%bukkit_version%)](#00fb9a)\n")
.append("[%plugin_description%](gray)\n") .append("[%plugin_description%](gray)\n")
.append("[• Author:](white) [William278](gray show_text=&7Click to pay a visit open_url=https://youtube.com/William27528)\n") .append("[• Author:](white) [William278](gray show_text=&7Click to visit website open_url=https://william278.net)\n")
.append("[• Contributors:](white) [HarvelsX](gray show_text=&7Code)\n") .append("[• Contributors:](white) [HarvelsX](gray show_text=&7Code)\n")
.append("[• Translators:](white) [Namiu/うにたろう](gray show_text=&7Japanese, ja-jp), [anchelthe](gray show_text=&7Spanish, es-es)\n")
.append("[• Plugin Info:](white) [[Link]](#00fb9a show_text=&7Click to open link open_url=https://github.com/WiIIiam278/HuskSync/)\n") .append("[• Plugin Info:](white) [[Link]](#00fb9a show_text=&7Click to open link open_url=https://github.com/WiIIiam278/HuskSync/)\n")
.append("[• Report Issues:](white) [[Link]](#00fb9a show_text=&7Click to open link open_url=https://github.com/WiIIiam278/HuskSync/issues)\n") .append("[• Report Issues:](white) [[Link]](#00fb9a show_text=&7Click to open link open_url=https://github.com/WiIIiam278/HuskSync/issues)\n")
.append("[• Support Discord:](white) [[Link]](#00fb9a show_text=&7Click to join open_url=https://discord.gg/tVYhJfyDWG)"); .append("[• Support Discord:](white) [[Link]](#00fb9a show_text=&7Click to join open_url=https://discord.gg/tVYhJfyDWG)");
public static StringBuilder PLUGIN_STATUS = new StringBuilder().append("[HuskSync](#00fb9a bold) [| Current system status:](#00fb9a)\n") public static StringBuilder PLUGIN_STATUS = new StringBuilder().append("[HuskSync](#00fb9a bold) [| Current system status:](#00fb9a)\n")
.append("[• Connected servers:](white) [%1%](#00fb9a)") .append("[• Connected servers:](white) [%1%](#00fb9a)\n")
.append("[• Cached player data:](white) [%2%](#00fb9a)"); .append("[• Cached player data:](white) [%2%](#00fb9a)");
} }

View File

@@ -11,38 +11,35 @@ public abstract class UpdateChecker {
private final static int SPIGOT_PROJECT_ID = 97144; private final static int SPIGOT_PROJECT_ID = 97144;
private final String currentVersion; private final VersionUtils.Version currentVersion;
private String latestVersion; private VersionUtils.Version latestVersion;
public UpdateChecker(String currentVersion) { public UpdateChecker(String currentVersion) {
this.currentVersion = currentVersion; this.currentVersion = VersionUtils.Version.of(currentVersion);
try { try {
final URL url = new URL("https://api.spigotmc.org/legacy/update.php?resource=" + SPIGOT_PROJECT_ID); final URL url = new URL("https://api.spigotmc.org/legacy/update.php?resource=" + SPIGOT_PROJECT_ID);
URLConnection urlConnection = url.openConnection(); URLConnection urlConnection = url.openConnection();
this.latestVersion = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())).readLine(); this.latestVersion = VersionUtils.Version.of(new BufferedReader(new InputStreamReader(urlConnection.getInputStream())).readLine());
} catch (IOException e) { } catch (IOException e) {
log(Level.WARNING, "Failed to check for updates: An IOException occurred."); log(Level.WARNING, "Failed to check for updates: An IOException occurred.");
this.latestVersion = "Unknown"; this.latestVersion = new VersionUtils.Version();
} catch (Exception e) { } catch (Exception e) {
log(Level.WARNING, "Failed to check for updates: An exception occurred."); log(Level.WARNING, "Failed to check for updates: An exception occurred.");
this.latestVersion = "Unknown"; this.latestVersion = new VersionUtils.Version();
} }
} }
public boolean isUpToDate() { public boolean isUpToDate() {
if (latestVersion.equalsIgnoreCase("Unknown")) { return this.currentVersion.compareTo(latestVersion) >= 0;
return true;
}
return latestVersion.equals(currentVersion);
} }
public String getLatestVersion() { public String getLatestVersion() {
return latestVersion; return latestVersion.toString();
} }
public String getCurrentVersion() { public String getCurrentVersion() {
return currentVersion; return currentVersion.toString();
} }
public abstract void log(Level level, String message); public abstract void log(Level level, String message);

View File

@@ -0,0 +1,61 @@
package me.william278.husksync.util;
import java.util.Arrays;
public class VersionUtils {
private final static char META_SEPARATOR = '+';
private final static String VERSION_SEPARATOR = "\\.";
public static class Version implements Comparable<Version> {
public int[] versions = new int[]{};
public String metadata = "";
public Version() {
}
public Version(String version) {
this.parse(version);
}
public static Version of(String version) {
return new Version(version);
}
private void parse(String version) {
int metaIndex = version.indexOf(META_SEPARATOR);
if (metaIndex > 0) {
this.metadata = version.substring(metaIndex + 1);
version = version.substring(0, metaIndex);
}
String[] versions = version.split(VERSION_SEPARATOR);
this.versions = Arrays.stream(versions).mapToInt(Integer::parseInt).toArray();
}
@Override
public int compareTo(Version version) {
int length = Math.max(this.versions.length, version.versions.length);
for (int i = 0; i < length; i++) {
int a = i < this.versions.length ? this.versions[i] : 0;
int b = i < version.versions.length ? version.versions[i] : 0;
if (a < b) return -1;
if (a > b) return 1;
}
return 0;
}
@Override
public String toString() {
StringBuilder stringBuffer = new StringBuilder();
for (int version : this.versions) {
stringBuffer.append(version).append('.');
}
stringBuffer.deleteCharAt(stringBuffer.length() - 1);
return stringBuffer.append('+').append(this.metadata).toString();
}
}
}

View File

@@ -0,0 +1,14 @@
synchronisation_complete: '[Datos sincronizados!](#00fb9a)'
viewing_inventory_of: '[Viendo el inventario de](#00fb9a) [%1%](#00fb9a bold)'
viewing_ender_chest_of: '[Viendo el Ender Chest de](#00fb9a) [%1%](#00fb9a bold)'
reload_complete: '[HuskSync](#00fb9a bold) [| Se ha reiniciado la configuración y los archivos de los mensajes.](#00fb9a)'
error_invalid_syntax: '[Error:](#ff3300) [Sintaxis incorrecta. Uso: %1%](#ff7e5e)'
error_invalid_player: '[Error:](#ff3300) [No se ha podido encontrar a ese jugador](#ff7e5e)'
error_no_permission: '[Error:](#ff3300) [No tienes permiso para ejecutar este comando](#ff7e5e)'
error_cannot_view_inventory_online: '[Error:](#ff3300) [A traves de HuskSync no puedes acceder al inventario de un jugador conectado](#ff7e5e)'
error_cannot_view_ender_chest_online: '[Error:](#ff3300) [A traves de HuskSync no puedes acceder al Ender Chest de un jugador conectado.](#ff7e5e)'
error_cannot_view_own_inventory: '[Error:](#ff3300) [No puedes acceder a tu inventario!](#ff7e5e)'
error_cannot_view_own_ender_chest: '[Error:](#ff3300) [No puedes acceder a tu Ender Chest!](#ff7e5e)'
error_console_command_only: '[Error:](#ff3300) [Ese comando solo puede ser ejecutado desde la %1% consola](#ff7e5e)'
error_no_servers_proxied: '[Error:](#ff3300) [Ha ocurrido un error mientras se procesaba la acción; no hay servidores online con HusckSync instalado. Por favor, asegúrate que HuskSync está instalado tanto en el proxy como en todos los servidores entre los que quieres sincronizar datos.](#ff7e5e)'
error_invalid_cluster: '[Error:](#ff3300) [Por favor, especifica la ID de un cluster válido.](#ff7e5e)'

View File

@@ -0,0 +1,14 @@
synchronisation_complete: '[データが同期されました!](#00fb9a)'
viewing_inventory_of: '[%1%](#00fb9a bold) [のインベントリを表示します](#00fb9a) '
viewing_ender_chest_of: '[%1%](#00fb9a bold) [のエンダーチェストを表示します](#00fb9a) '
reload_complete: '[HuskSync](#00fb9a bold) [| 設定ファイルとメッセージファイルを再読み込みしました。](#00fb9a)'
error_invalid_syntax: '[Error:](#ff3300) [構文が正しくありません。使用法: %1%](#ff7e5e)'
error_invalid_player: '[Error:](#ff3300) [そのプレイヤーは見つかりませんでした](#ff7e5e)'
error_no_permission: '[Error:](#ff3300) [このコマンドを実行する権限がありません](#ff7e5e)'
error_cannot_view_inventory_online: '[Error:](#ff3300) [HuskSyncからオンラインプレイヤーのインベントリにはアクセスできません](#ff7e5e)'
error_cannot_view_ender_chest_online: '[Error:](#ff3300) [HuskSyncからオンラインプレイヤーのエンダーチェストにはアクセスできません](#ff7e5e)'
error_cannot_view_own_inventory: '[Error:](#ff3300) [自分のインベントリにはアクセスできません!](#ff7e5e)'
error_cannot_view_own_ender_chest: '[Error:](#ff3300) [自分のエンダーチェストにはアクセスできません!](#ff7e5e)'
error_console_command_only: '[Error:](#ff3300) [そのコマンドは%1%コンソールからのみ実行できます](#ff7e5e)'
error_no_servers_proxied: '[Error:](#ff3300) [操作の処理に失敗; HuskSyncがインストールされているサーバーがオンラインになっていません。プロキシサーバーとデータを同期させたいすべてのサーバーにHuskSyncがインストールされていることを確認してください。](#ff7e5e)'
error_invalid_cluster: '[Error:](#ff3300) [有効なクラスターのIDを指定してください。](#ff7e5e)'

View File

@@ -3,6 +3,7 @@ redis_settings:
host: 'localhost' host: 'localhost'
port: 6379 port: 6379
password: '' password: ''
use_ssl: false
data_storage_settings: data_storage_settings:
database_type: 'sqlite' database_type: 'sqlite'
mysql_settings: mysql_settings:

View File

@@ -1 +1,5 @@
javaVersion=16 org.gradle.parallel=true
javaVersion=16
plugin_version=1.3.2
plugin_archive=husksync

View File

@@ -1,30 +1,27 @@
//file:noinspection GroovyAssignabilityCheck
plugins {
id 'maven-publish'
}
dependencies { dependencies {
implementation project(path: ":common", configuration: 'shadow') implementation project(path: ':bukkit', configuration: 'shadow')
implementation project(path: ":api", configuration: 'shadow') implementation project(path: ':bungeecord', configuration: 'shadow')
implementation project(path: ":bukkit", configuration: 'shadow') implementation project(path: ':velocity', configuration: 'shadow')
implementation project(path: ":bungeecord", configuration: 'shadow')
implementation project(path: ":velocity", configuration: 'shadow')
} }
shadowJar { shadowJar {
// Relocations dependencies {
relocate 'redis.clients', 'me.William278.husksync.libraries.jedis' exclude dependency(':jedis')
exclude dependency(':commons-pool2')
destinationDirectory.set(file("$rootDir/target/"))
archiveBaseName.set('HuskSync')
archiveClassifier.set('')
build {
dependsOn tasks.named("shadowJar")
} }
} }
publishing { publishing {
publications { publications {
mavenJava(MavenPublication) { mavenJava(MavenPublication) {
groupId = 'me.William278' groupId = 'me.william278'
artifactId = 'HuskSync-plugin' artifactId = 'husksync-plugin'
version = "$project.version" version = "$rootProject.version"
artifact shadowJar artifact shadowJar
} }

View File

@@ -1,21 +1,26 @@
dependencies { dependencies {
compileOnly project(':common')
implementation project(path: ':common', configuration: 'shadow') implementation project(path: ':common', configuration: 'shadow')
compileOnly 'redis.clients:jedis:3.7.1' implementation 'com.zaxxer:HikariCP:5.0.1'
implementation 'org.bstats:bstats-velocity:2.2.1' implementation 'org.bstats:bstats-velocity:3.0.0'
implementation 'de.themoep:minedown-adventure:1.7.1-SNAPSHOT' implementation 'de.themoep:minedown-adventure:1.7.1-SNAPSHOT'
implementation 'net.byteflux:libby-velocity:1.1.5' implementation 'net.byteflux:libby-velocity:1.1.5'
compileOnly 'com.velocitypowered:velocity-api:3.1.0' compileOnly 'com.velocitypowered:velocity-api:3.1.0'
annotationProcessor 'com.velocitypowered:velocity-api:3.1.0'
} }
shadowJar { shadowJar {
relocate 'com.zaxxer', 'me.William278.husksync.libraries.hikari' relocate 'de.themoep', 'me.william278.husksync.libraries'
relocate 'org.bstats', 'me.William278.husksync.libraries.bstats.velocity' relocate 'net.byteflux', 'me.william278.husksync.libraries'
relocate 'de.themoep', 'me.William278.husksync.libraries.minedown.adventure' relocate 'org.bstats', 'me.william278.husksync.libraries.bstats'
relocate 'net.byteflux', 'me.William278.husksync.libraries.libby.velocity'
}
tasks.register('prepareKotlinBuildScriptModel'){} relocate 'redis.clients', 'me.william278.husksync.libraries'
relocate 'org.apache', 'me.william278.husksync.libraries'
relocate 'com.zaxxer', 'me.william278.husksync.libraries'
dependencies {
//noinspection GroovyAssignabilityCheck
exclude dependency(':slf4j-api')
}
}

View File

@@ -7,6 +7,7 @@ import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent; import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
import com.velocitypowered.api.event.proxy.ProxyShutdownEvent; import com.velocitypowered.api.event.proxy.ProxyShutdownEvent;
import com.velocitypowered.api.plugin.Plugin; import com.velocitypowered.api.plugin.Plugin;
import com.velocitypowered.api.plugin.PluginContainer;
import com.velocitypowered.api.plugin.annotation.DataDirectory; import com.velocitypowered.api.plugin.annotation.DataDirectory;
import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.api.proxy.ProxyServer;
import me.william278.husksync.migrator.MPDBMigrator; import me.william278.husksync.migrator.MPDBMigrator;
@@ -31,19 +32,11 @@ import java.util.HashSet;
import java.util.Objects; import java.util.Objects;
import java.util.logging.Level; import java.util.logging.Level;
import static me.william278.husksync.HuskSyncVelocity.VERSION; @Plugin(id = "husksync")
@Plugin(
id = "husksync",
name = "HuskSync",
version = VERSION,
description = "HuskSync for velocity",
authors = {"William278"}
)
public class HuskSyncVelocity { public class HuskSyncVelocity {
// Plugin version // Plugin version
public static final String VERSION = "1.3"; public static String VERSION = null;
// Velocity bStats ID (different from Bukkit and BungeeCord) // Velocity bStats ID (different from Bukkit and BungeeCord)
private static final int METRICS_ID = 13489; private static final int METRICS_ID = 13489;
@@ -68,6 +61,8 @@ public class HuskSyncVelocity {
public static DataManager dataManager; public static DataManager dataManager;
public static VelocityRedisListener redisListener;
public static MPDBMigrator mpdbMigrator; public static MPDBMigrator mpdbMigrator;
private final Logger logger; private final Logger logger;
@@ -92,11 +87,13 @@ public class HuskSyncVelocity {
} }
@Inject @Inject
public HuskSyncVelocity(ProxyServer server, Logger logger, @DataDirectory Path dataDirectory, Metrics.Factory metricsFactory) { public HuskSyncVelocity(ProxyServer server, Logger logger, @DataDirectory Path dataDirectory, Metrics.Factory metricsFactory, PluginContainer pluginContainer) {
this.server = server; this.server = server;
this.logger = logger; this.logger = logger;
this.dataDirectory = dataDirectory; this.dataDirectory = dataDirectory;
this.metricsFactory = metricsFactory; this.metricsFactory = metricsFactory;
pluginContainer.getDescription().getVersion().ifPresent(s -> VERSION = s);
} }
@Subscribe @Subscribe
@@ -145,10 +142,7 @@ public class HuskSyncVelocity {
} }
// Initialize the redis listener // Initialize the redis listener
if (!new VelocityRedisListener().isActiveAndEnabled) { redisListener = new VelocityRedisListener();
getVelocityLogger().severe("Failed to initialize Redis; HuskSync will now abort loading itself (Velocity) v" + VERSION);
return;
}
// Register listener // Register listener
server.getEventManager().register(this, new VelocityEventListener()); server.getEventManager().register(this, new VelocityEventListener());

View File

@@ -294,7 +294,7 @@ public class VelocityCommand implements SimpleCommand, HuskSyncCommand {
HuskSyncVelocity.synchronisedServers)) { HuskSyncVelocity.synchronisedServers)) {
plugin.getProxyServer().getScheduler().buildTask(plugin, () -> plugin.getProxyServer().getScheduler().buildTask(plugin, () ->
HuskSyncVelocity.mpdbMigrator.executeMigrationOperations(HuskSyncVelocity.dataManager, HuskSyncVelocity.mpdbMigrator.executeMigrationOperations(HuskSyncVelocity.dataManager,
HuskSyncVelocity.synchronisedServers)).schedule(); HuskSyncVelocity.synchronisedServers, HuskSyncVelocity.redisListener)).schedule();
} }
} }
default -> sender.sendMessage(new MineDown("Error: Invalid argument for migration. Use \"husksync migrate\" to start the process").toComponent()); default -> sender.sendMessage(new MineDown("Error: Invalid argument for migration. Use \"husksync migrate\" to start the process").toComponent());

View File

@@ -31,25 +31,24 @@ public class ConfigLoader {
} }
private static String getConfigString(ConfigurationNode rootNode, String defaultValue, String... nodePath) { private static String getConfigString(ConfigurationNode rootNode, String defaultValue, String... nodePath) {
return !rootNode.getNode((Object[]) nodePath).isVirtual() ? rootNode.getNode((Object[])nodePath).getString() : defaultValue; return !rootNode.getNode((Object[]) nodePath).isVirtual() ? rootNode.getNode((Object[]) nodePath).getString() : defaultValue;
} }
@SuppressWarnings("SameParameterValue") @SuppressWarnings("SameParameterValue")
private static boolean getConfigBoolean(ConfigurationNode rootNode, boolean defaultValue, String... nodePath) { private static boolean getConfigBoolean(ConfigurationNode rootNode, boolean defaultValue, String... nodePath) {
return !rootNode.getNode((Object[]) nodePath).isVirtual() ? rootNode.getNode((Object[])nodePath).getBoolean() : defaultValue; return !rootNode.getNode((Object[]) nodePath).isVirtual() ? rootNode.getNode((Object[]) nodePath).getBoolean() : defaultValue;
} }
private static int getConfigInt(ConfigurationNode rootNode, int defaultValue, String... nodePath) { private static int getConfigInt(ConfigurationNode rootNode, int defaultValue, String... nodePath) {
return !rootNode.getNode((Object[]) nodePath).isVirtual() ? rootNode.getNode((Object[])nodePath).getInt() : defaultValue; return !rootNode.getNode((Object[]) nodePath).isVirtual() ? rootNode.getNode((Object[]) nodePath).getInt() : defaultValue;
} }
private static long getConfigLong(ConfigurationNode rootNode, long defaultValue, String... nodePath) { private static long getConfigLong(ConfigurationNode rootNode, long defaultValue, String... nodePath) {
return !rootNode.getNode((Object[])nodePath).isVirtual() ? rootNode.getNode((Object[])nodePath).getLong() : defaultValue; return !rootNode.getNode((Object[]) nodePath).isVirtual() ? rootNode.getNode((Object[]) nodePath).getLong() : defaultValue;
} }
public static void loadSettings(ConfigurationNode loadedConfig) throws IllegalArgumentException { public static void loadSettings(ConfigurationNode loadedConfig) throws IllegalArgumentException {
ConfigurationNode config = copyDefaults(loadedConfig); ConfigurationNode config = copyDefaults(loadedConfig);
//ConfigurationNode config = copyDefaults(loadedConfig);
Settings.language = getConfigString(config, "en-gb", "language"); Settings.language = getConfigString(config, "en-gb", "language");
@@ -58,6 +57,7 @@ public class ConfigLoader {
Settings.redisHost = getConfigString(config, "localhost", "redis_settings", "host"); Settings.redisHost = getConfigString(config, "localhost", "redis_settings", "host");
Settings.redisPort = getConfigInt(config, 6379, "redis_settings", "port"); Settings.redisPort = getConfigInt(config, 6379, "redis_settings", "port");
Settings.redisPassword = getConfigString(config, "", "redis_settings", "password"); Settings.redisPassword = getConfigString(config, "", "redis_settings", "password");
Settings.redisSSL = getConfigBoolean(config, false, "redis_settings", "use_ssl");
Settings.dataStorageType = Settings.DataStorageType.valueOf(getConfigString(config, "sqlite", "data_storage_settings", "database_type").toUpperCase()); Settings.dataStorageType = Settings.DataStorageType.valueOf(getConfigString(config, "sqlite", "data_storage_settings", "database_type").toUpperCase());
if (Settings.dataStorageType == Settings.DataStorageType.MYSQL) { if (Settings.dataStorageType == Settings.DataStorageType.MYSQL) {

View File

@@ -23,6 +23,7 @@ public class VelocityRedisListener extends RedisListener {
// Initialize the listener on the bungee // Initialize the listener on the bungee
public VelocityRedisListener() { public VelocityRedisListener() {
super();
listen(); listen();
} }

View File

@@ -0,0 +1,12 @@
{
"id": "husksync",
"name": "HuskSync",
"version": "${version}",
"description": "A modern, cross-server player data synchronization system",
"url": "https://william278.net",
"authors": [
"William278"
],
"dependencies": [],
"main": "me.william278.husksync.HuskSyncVelocity"
}