diff --git a/LICENSE b/LICENSE
index b9cd78d..2c87bac 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,21 +1,18 @@
MIT License
-Copyright (c) 2025 D.L.
+Copyright (c) 2025 deutschich
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
+associated documentation files (the "Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
+following conditions:
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
+The above copyright notice and this permission notice shall be included in all copies or substantial
+portions of the Software.
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
+EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/README.md b/README.md
index f544e30..9797da6 100644
--- a/README.md
+++ b/README.md
@@ -1,92 +1,3 @@
# BalSync
-BalSync is a powerful and reliable balance synchronization system for Minecraft servers. It ensures that player balances are securely stored, automatically updated, and consistently synchronized across your server network.
-
----
-
-## ๐ฏ Features
-
-### Automatic Balance Backup
-- Player balances are automatically saved to a MySQL database at configurable intervals.
-- No manual intervention required โ everything runs seamlessly in the background.
-
-### Seamless Login Synchronization
-- Player balances are automatically loaded from the database when they join the server.
-- Optional: Reset balances to 0 before loading from the database (ideal for events or test servers).
-
-### Real-Time Monitoring
-- The plugin regularly checks the database for external changes (e.g., made by admins or other systems).
-- Any changes are immediately applied to online players.
-
-### Intelligent Offline Detection
-- Changes to a player's balance are recognized even while they are offline.
-- Example: If a player earns money in single-player mode, it is updated when they join the server.
-
-### Multi-Language Notifications
-- Players are notified of important balance changes.
-- Supports 7 languages: German, English, Spanish, French, Polish, Portuguese (Brazil), Russian.
-
----
-
-## ๐ฎ Player Experience
-
-- On server join: *"Your balance has been synchronized with the database."*
-- On external updates: *"Your balance was updated externally: 100 โ 150"*
-- No data loss: Balances are always safely stored.
-- Server switching supported: Players can move between servers and retain their balances.
-
----
-
-## ๐จโ๐ผ Admin Commands
-
-| Command | Description |
-|--------------------------|-----------------------------------------|
-| `/balsync reload` | Reloads plugin configuration |
-| `/balsync save` | Immediately saves all player balances |
-| `/balsync load` | Reload your own balance from the database |
-| `/balsync status` | Displays system status |
-
----
-
-## โ๏ธ Configuration Options
-
-- Set automatic save intervals (e.g., every minute)
-- Enable or disable notifications
-- Configure database polling intervals
-- Set starting balance for new players
-- Customize the database table name
-
----
-
-## ๐ Security & Performance
-
-- All transactions are logged
-- Database connection supports SSL
-- Connection pooling for optimal performance
-- Fault-tolerant architecture ensures reliability
-
----
-
-## ๐ Supported Platforms
-
-- Paper
-- Spigot
-- Purpur
-- And other compatible Minecraft server forks
-
----
-
-## ๐ Getting Started
-
-1. Place the `BalSync.jar` file into your server's `plugins` folder.
-2. Start the server once to generate the default configuration file.
-3. Configure your MySQL database credentials and plugin settings in `config.yml`.
-4. Restart the server to apply changes.
-5. Enjoy secure, automatic balance synchronization for all your players!
-
----
-
-## ๐ฌ Feedback & Support
-
-If you encounter issues or have feature suggestions, please open an issue on GitHub. Community contributions are welcome!
-
+An open source Minecraft Economy Balance Synchronisation Plugin working with a MySQL/MariaDB Database and Vault
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 70a6ae3..f552a8a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
com.user404_
BalSync
- 1.0
+ 1.1
jar
BalSync
diff --git a/src/main/java/com/user404_/balsync/BalanceManager.java b/src/main/java/com/user404_/balsync/BalanceManager.java
index 6849c98..f1af05f 100644
--- a/src/main/java/com/user404_/balsync/BalanceManager.java
+++ b/src/main/java/com/user404_/balsync/BalanceManager.java
@@ -21,6 +21,7 @@ public class BalanceManager {
private BukkitTask dbPollingTask;
private final Map lastKnownBalances = new HashMap<>();
private final Map lastKnownDbBalances = new HashMap<>();
+
public void saveAllBalances() {
plugin.getPluginLogger().info("Saving all player balances to database...");
@@ -43,6 +44,7 @@ public class BalanceManager {
plugin.getPluginLogger().info("Saved " + saved + " player balances to database.");
});
}
+
public BalanceManager(BalSyncPlugin plugin, Economy economy, DatabaseManager databaseManager) {
this.plugin = plugin;
this.economy = economy;
@@ -227,18 +229,32 @@ public class BalanceManager {
Double lastBalance = lastKnownBalances.get(uuid);
if (lastBalance != null && Math.abs(currentBalance - lastBalance) > 0.001) {
- // Balance has changed - save to database
+ // Balance has changed on this server. Instead of overwriting the DB with
+ // a stale server value, compute the delta and apply that to the DB.
+ double delta = currentBalance - lastBalance;
try {
- databaseManager.saveBalance(uuid, offlinePlayer.getName(), currentBalance);
+ databaseManager.addBalanceDelta(uuid, offlinePlayer.getName(), delta);
+
+ // Refresh DB snapshot for tracking
+ double newDbBalance = databaseManager.getBalance(uuid);
+
lastKnownBalances.put(uuid, currentBalance);
+ lastKnownDbBalances.put(uuid, newDbBalance);
+
plugin.getLogger().info("Detected offline change for " +
- offlinePlayer.getName() + ": " + currentBalance);
+ offlinePlayer.getName() + ": serverDelta=" + delta + ", newDB=" + newDbBalance);
+
+ // Optional: send configured offline-change message to console/log
+ String msg = plugin.getTranslationManager().getMessage("offline-change-detected");
+ if (msg != null && !msg.isEmpty()) {
+ plugin.getLogger().info(plugin.getTranslationManager().formatMessage("prefix") + msg);
+ }
} catch (SQLException e) {
- plugin.getLogger().log(Level.WARNING,
+ plugin.getPluginLogger().log(Level.WARNING,
"Failed to save offline change for " + offlinePlayer.getName(), e);
}
} else if (lastBalance == null) {
- // First time seeing this player, store initial balance
+ // First time seeing this player on this server, store initial balance
lastKnownBalances.put(uuid, currentBalance);
}
}
@@ -264,6 +280,23 @@ public class BalanceManager {
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
try {
double balance = economy.getBalance(player);
+ UUID uuid = player.getUniqueId();
+
+ Double lastBalance = lastKnownBalances.get(uuid);
+ if (lastBalance != null) {
+ // There is a last-known server-side snapshot => compute delta and apply it
+ double delta = balance - lastBalance;
+ if (Math.abs(delta) > 0.001) {
+ databaseManager.addBalanceDelta(uuid, player.getName(), delta);
+
+ double newDb = databaseManager.getBalance(uuid);
+ lastKnownBalances.put(uuid, balance);
+ lastKnownDbBalances.put(uuid, newDb);
+ return;
+ }
+ }
+
+ // Fallback: overwrite DB with current balance
databaseManager.saveBalance(player.getUniqueId(), player.getName(), balance);
lastKnownBalances.put(player.getUniqueId(), balance);
lastKnownDbBalances.put(player.getUniqueId(), balance);
diff --git a/src/main/java/com/user404_/balsync/DatabaseManager.java b/src/main/java/com/user404_/balsync/DatabaseManager.java
index c937421..6e02f81 100644
--- a/src/main/java/com/user404_/balsync/DatabaseManager.java
+++ b/src/main/java/com/user404_/balsync/DatabaseManager.java
@@ -64,7 +64,7 @@ public class DatabaseManager {
"`id` INT AUTO_INCREMENT PRIMARY KEY, " +
"`player_uuid` CHAR(36) UNIQUE NOT NULL, " +
"`player_name` VARCHAR(16), " +
- "`balance` DECIMAL(15, 2) NOT NULL DEFAULT %.2f, " + // DECIMAL fรผr bessere Prรคzision
+ "`balance` DECIMAL(15, 2) NOT NULL DEFAULT %.2f, " +
"`last_updated` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
"INDEX `idx_uuid` (`player_uuid`)" +
") CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE=InnoDB",
@@ -111,7 +111,7 @@ public class DatabaseManager {
"`id` INT AUTO_INCREMENT PRIMARY KEY, " +
"`player_uuid` CHAR(36) UNIQUE NOT NULL, " +
"`player_name` VARCHAR(16), " +
- "`balance` DECIMAL(15, 2) NOT NULL, " + // Kein DEFAULT hier
+ "`balance` DECIMAL(15, 2) NOT NULL, " +
"`last_updated` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
"INDEX `idx_uuid` (`player_uuid`)" +
") CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE=InnoDB",
@@ -158,6 +158,36 @@ public class DatabaseManager {
}
}
+ /**
+ * Atomically add a delta to the stored balance. This avoids overwriting the DB with stale
+ * values when multiple servers write concurrently. If no row exists, insert one with
+ * (startingBalance + delta).
+ */
+ public void addBalanceDelta(UUID playerUUID, String playerName, double delta) throws SQLException {
+ String updateSql = String.format("UPDATE %s SET balance = balance + ? WHERE player_uuid = ?", tableName);
+
+ try (Connection conn = dataSource.getConnection()) {
+ // Try atomic update first
+ try (PreparedStatement update = conn.prepareStatement(updateSql)) {
+ update.setDouble(1, delta);
+ update.setString(2, playerUUID.toString());
+ int affected = update.executeUpdate();
+
+ if (affected == 0) {
+ // No row existed โ insert with startingBalance + delta
+ double starting = plugin.getConfigManager().getStartingBalance();
+ String insertSql = String.format("INSERT INTO %s (player_uuid, player_name, balance) VALUES (?, ?, ?)", tableName);
+ try (PreparedStatement insert = conn.prepareStatement(insertSql)) {
+ insert.setString(1, playerUUID.toString());
+ insert.setString(2, playerName);
+ insert.setDouble(3, starting + delta);
+ insert.executeUpdate();
+ }
+ }
+ }
+ }
+ }
+
public Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml
index a9be692..53f385f 100644
--- a/src/main/resources/config.yml
+++ b/src/main/resources/config.yml
@@ -8,7 +8,7 @@ settings:
save-on-quit: true
# Starting balance for new players
starting-balance: 100.0
- # Locale for messages (en, de, etc.)
+ # Locale for messages (en, de, etc.), List of supported languages/locales can be found at https://github.com/deutschich/BalSync/wiki/Languages#supported-languages.
locale: "en"
# Reset to zero before loading from database on join
diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml
index 5c3704e..7fca260 100644
--- a/src/main/resources/plugin.yml
+++ b/src/main/resources/plugin.yml
@@ -1,5 +1,5 @@
name: BalSync
-version: 1.0
+version: 1.1
main: com.user404_.balsync.BalSyncPlugin
api-version: '1.20'
description: Synchronizes player balances with MySQL database