From 9d5cc73e0b3c8922f2c7d3d44b53926d539d0a63 Mon Sep 17 00:00:00 2001 From: deutschich Date: Wed, 3 Dec 2025 20:20:50 +0100 Subject: [PATCH 1/4] Added note to the Language Description in the config.yml --- src/main/resources/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 0af9fe4ffd2a2309090a23bccdeca7fa0a2830c2 Mon Sep 17 00:00:00 2001 From: deutschich Date: Tue, 2 Dec 2025 08:33:31 +0100 Subject: [PATCH 2/4] Initial commit 3 --- README.md | 91 +------------------------------------------------------ 1 file changed, 1 insertion(+), 90 deletions(-) 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 From 355b3cdc8f14c2d341c2be635495d10f287525bd Mon Sep 17 00:00:00 2001 From: deutschich Date: Tue, 2 Dec 2025 08:33:31 +0100 Subject: [PATCH 3/4] Initial commit 2 --- LICENSE | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) 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. From 6b0c22b71f8c919cd0f738cf3b218a9c078d9ed8 Mon Sep 17 00:00:00 2001 From: deutschich Date: Thu, 4 Dec 2025 17:23:55 +0100 Subject: [PATCH 4/4] Fixed the first Duplication Glitch with this Plugin and released 1.1. --- pom.xml | 2 +- .../com/user404_/balsync/BalanceManager.java | 43 ++++++++++++++++--- .../com/user404_/balsync/DatabaseManager.java | 34 ++++++++++++++- src/main/resources/plugin.yml | 2 +- 4 files changed, 72 insertions(+), 9 deletions(-) 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/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